
What Checkout Shipping Does
Checkout Shipping is the companion piece to Checkout Billing: it isolates only the shipping-address fieldset of the WooCommerce checkout page into its own Elementor widget, so it can be placed, resized, and styled independently of the rest of the checkout form. On the default WooCommerce checkout template, shipping fields are just another block wedged between billing and payment with no way to move them without editing template files. As a standalone widget, Shipping can be dropped into its own column, hidden behind a tab, placed under a “ship to a different address” toggle styled to match the site, or positioned however a custom multi-column checkout layout calls for — while still being driven entirely by WooCommerce’s own shipping-address logic underneath.
Setting It Up
The Content tab exposes the same lightweight pattern as Billing: a Heading text control (default “Shipping details”, dynamic-tag capable) and a Show Heading switcher to hide the label when your layout already communicates the section with surrounding design elements. There is no separate heading-style section on this widget — instead it inherits the shared Fields style section from the base checkout class directly: label color, label typography, field background, field text color, field border color, responsive field padding, and a field-radius slider, all scoped to the widget’s own inputs, textareas, and select2 dropdowns. Because Billing and Shipping both expose this identical Fields section, giving them matching values is the fastest way to make two separately-placed widgets read as one continuous form.
It Renders WooCommerce’s Own Template — Not a Rebuild
Just like Billing, this widget does not reimplement WooCommerce’s shipping form from scratch. It calls the shared ensure_checkout_setup() helper to bootstrap WC()->checkout(), recalculates cart totals if the cart isn’t empty, and then fires the native woocommerce_checkout_shipping action — the exact same hook the stock checkout page relies on. Every filter already wired into that hook keeps functioning: woocommerce_checkout_fields customizations to the shipping fieldset, third-party address-validation or autocomplete integrations, country/state conditional field logic, and any shipping-specific compliance fields a plugin injects all continue to work unmodified. This widget’s only job is deciding where that native block sits on the page and how it’s styled — none of WooCommerce’s core shipping-address behavior is touched.
A Built-In Guard: It Hides Itself When Shipping Isn’t Needed
One behavior worth calling out that’s specific to this widget (and not shared with Billing) is its shipping-relevance check. Before rendering anything, it calls WC()->cart->needs_shipping_address(), and if the current cart contains only virtual or downloadable products, the widget renders a placeholder message instead of an empty fieldset. This mirrors exactly how the native WooCommerce checkout page behaves — it never shows shipping fields for a cart that doesn’t need them — so a builder doesn’t need to add manual Elementor conditions to hide this widget for digital-goods carts; the widget already handles that itself at render time, based on the real contents of the shopper’s cart.
Shipping vs. Billing vs. Payment vs. Order Review
Shipping’s role in the four-widget checkout family is narrower and more conditional than Billing’s: Billing is always relevant because every order has a payer, while Shipping only matters when physical delivery is involved. In a typical layout, Shipping sits directly below or beside Billing in the same column, often behind a “ship to a different address” checkbox rendered by WooCommerce itself as part of the billing fieldset’s native markup. Payment and Order Review remain separate widgets further down or in a second column — Payment carries the actual gateway selection and the Place Order submit button, and Order Review shows the live cart/tax/shipping-method summary. Keeping Shipping as its own widget rather than merging it into Billing means a builder can give it a different background, different column width, or hide it completely on layouts built exclusively for a digital-only storefront.
Practical Tips
- Don’t be alarmed if the widget shows nothing but a placeholder in the editor while testing with a virtual-product-only cart — that’s the built-in
needs_shipping_address()guard working correctly, not a bug. - Place Shipping directly beneath Billing in the same column; WooCommerce’s native “ship to a different address” toggle lives inside the billing markup and expects the shipping fields to be nearby in the DOM for its show/hide JavaScript to feel natural to shoppers.
- Match the Fields style values (field background, border color, radius) exactly between Billing and Shipping instances — since they’re separate widgets, Elementor won’t sync these for you automatically.
- If you run a mixed store (physical and digital products), always test the checkout layout with both a physical-item cart and a digital-only cart, since this widget’s visible output genuinely differs between the two.