Add Shipping Address for Virtual Products in WooCommerce

I came across a project recently that needed to offer a variety of products that offered free shipping, while other products in their WooCommerce store needed to have a shipping cost associated with them. This can be a bit tricky, and one of the simplest solutions is to simply mark any products that you want to have free shipping as a ‘Virtual Product’.

WooCommerce is intuitive enough to know that typically a ‘virtual product’ would not need to be shipped, as it isn’t a tangible item, so they automatically remove the option to select a separate shipping address during checkout. In my case it was a tangible item so the user needed to keep the option for a shipping address. This brief tutorial will show you how to do that.

Add Shipping Option Back to Checkout Page

The first thing we need to do is add the option for a shipping address back to the checkout page. There are plenty of tutorials out there for this – simply add the following to your functions.php file:

add_filter( 'woocommerce_cart_needs_shipping_address', '__return_true', 50 );

This will add the option to select a separate shipping address during checkout. Simple enough, right? Is that all there is to it? Almost, but not quite.

Unfortunately, even though we have added this back to the checkout page, the information is still NOT included in the New Order notification that is sent out to the admin when a purchase is made. The information IS stored and can be looked up in the WordPress dashboard, but it really should be included in the email as well.

Add Shipping Information to New Order Notification

Disclaimer: the store I use this technique on is very small and this method is not tested thoroughly is a larger WooCommerce store environment!

To add the shipping information to the new order email we need to copy /templates/email/email-addresses.php from the WooCommerce plugin to your /woocommerce/emails folder in your theme or child theme. If you do not have a woocommerce folder in your theme or child theme, simply create one. Files in that folder will override the default WooCommerce files, as long as the structure is correct. As a side note, make sure you DO NOT edit core files in any plugins directly. You should always make changes in a child theme to ensure your changes are not overwritten if the plugin has an update.

In that file we need to make one change on line 26:

Change this:

if ( ! wc_ship_to_billing_address_only() && $order->needs_shipping_address() && ( $shipping = $order->get_formatted_shipping_address() ) ) : ?>

To this:

if ( ! wc_ship_to_billing_address_only() && ( $shipping = $order->get_formatted_shipping_address() ) ) : ?>

You’ll see we have removed a small part of the if statement that references the needs_shipping_address() function. By removing this, the shipping address is now included in the New Order email that is sent to admins after a new purchase from your WooCommerce Store.

Get New Posts in Your Inbox

This field is for validation purposes and should be left unchanged.

About Matt Whiteley

Matt has been building bespoke WordPress websites for over 10 years specializing in the Genesis Framework. He integrates Advanced Custom Fields heavily in all builds, especially with the new Gutenberg Block Editor, allowing clients to easily modify all aspects of the their website with ease.

He works with a wide variety of small businesses and agencies across the country providing development services as well as hosting and maintenance. When he isn't coding he enjoys spending time with his wife and two children, golfing and playing poker.

Reader Interactions


    • Matt Whiteley says

      Hey Reyna,

      Are you referring to the customer’s shipping address, which is what I’m referring to in the above tutorial, or are you referring to shipping details like the tracking number, etc…?



      • Reyna says

        Hi Matt

        Thanks for replying! In your tutorial, you showed that the shipping address can be added to the New Order notification order to the admin. I was wondering if this can be done for the customer side, as for my case, an email (processing email) is sent to the customer once a purchase is made. And after which we will manually approve with another email (confirmation email) which is to confirm their order. I was hoping the shipping address can be made to appear in both emails to the customer.

  1. Matt Whiteley says

    Hey Reyna,

    This method may work for what you are trying to accomplish. I haven’t looked in to modifying the customer-side emails as this project only required the admin to get the notification. I would say give this method a try. I won’t have time to look in to it for a while, but may revisit it in the future and will update you when/if I have a solution. You’re free to use the contact form to get in touch with me if you’d like a quote to look in to it further! Sorry I can’t of of more help right away!

  2. Lizzy says

    Is anyone having trouble with this? It is not creating a box to select a different shipping method in my site. Is it compatible with the Woocommerce 2.6.14?

    • Matt Whiteley says

      Hey Lizzy,

      This post is from over a year ago so I’m not 100% sure it would still apply. I know WooCommerce is on to version 3.0 now which is supposed to be a pretty big update with lots of changes, so I wouldn’t be surprised if this snippet is no longer applicable. If I have some free time I’ll see if I can test with 2.6.14 and get back to you!



      • Lizzy says

        Do you know of anything that might work for the current versions? realize this is an old post, but I’ve searched everywhere, and I can find anything recent.

        • Matt Whiteley says

          Unfortunately not without doing a bit of testing. I’ll see if I can toy around with things in the next week and update here if I find anything helpful.

  3. Ron says

    Try this code to make shipping appear in all emails and all completed order pages:

    function rgc_woocommerce_order_needs_shipping_address($needs_address, $hide, $orderObj) {

    // if already true, return
    if($needs_address) return true;

    // was shipping address added to order?
    if($shipping = $orderObj->get_formatted_shipping_address()) return true;

    return false;

    add_filter( ‘woocommerce_order_needs_shipping_address’, ‘rgc_woocommerce_order_needs_shipping_address’, 10, 4);

    • Matt Whiteley says

      Hey Ron – thanks for the tip. I’ll have to give this a shot and see how it works. Seems simplistic enough! Cheers.

      • Matt Whiteley says

        Yes. The other option would be doing it in a core functionality plugin so it is separate from the theme related things and could be used when themes are changed. That is likely a better option.

Leave a Reply

Your email address will not be published.