WooCommerce ships shortcodes for the four core pages — [woocommerce_cart], [woocommerce_checkout], [woocommerce_my_account], [woocommerce_order_tracking] — plus product display shortcodes like [products], [product_page], and [add_to_cart]. Most now have block equivalents, and the Cart and Checkout blocks in particular are where new development goes. [woocommerce_my_account] is the notable holdout with no block alternative.
So this is both a reference and a decision: shortcode or block. The answer depends on how much your checkout is customised, and that is not a small consideration.
Table of contents
- The four page shortcodes
- Displaying products
- Categories, single products, and buttons
- Blocks, and when to migrate
- Shortcodes in templates and blocks
- Performance
- How this fits the rest of the stack
- FAQ
The four page shortcodes
WooCommerce creates these pages at install and puts the right shortcode in each.
[woocommerce_cart]
[woocommerce_checkout]
[woocommerce_my_account]
[woocommerce_order_tracking]
[woocommerce_cart]— the cart page. Block alternative: Cart block.[woocommerce_checkout]— the checkout. Block alternative: Checkout block.[woocommerce_my_account]— the account area: orders, downloads, addresses, details. Acceptscurrent_userto display a specific user’s account. No block equivalent.[woocommerce_order_tracking]— a form to look up an order by ID and email. No block equivalent.
These must appear exactly once on their assigned page. Two checkout shortcodes on one page produce duplicate form fields and a checkout that fails validation in confusing ways.
If a page has gone blank, check the shortcode is still present — a theme change or an editor mishap that removes it leaves a page that looks empty rather than broken, which sends people looking in the wrong place.
Displaying products
[products] is the workhorse and replaced several older shortcodes.
[products limit="8" columns="4"]
[products limit="4" columns="4" orderby="date" order="DESC"]
[products category="hoodies,shirts" limit="8"]
[products ids="12,34,56"]
[products skus="SHIRT-01,SHIRT-02"]
[products on_sale="true" limit="8"]
[products best_selling="true" limit="4"]
[products top_rated="true" limit="4"]
[products attribute="colour" terms="blue" limit="8"]
[products tag="summer" limit="8"]
The attributes worth knowing:
limit— how many to show.-1for all, which you should avoid on a large catalogue.columns— grid columns, default 4.orderby—date,title,price,popularity,rating,menu_order,rand,id.order—ASCorDESC.paginate—trueto add pagination; needslimitset.visibility—visible,catalog,search,hidden,featured.class— a CSS class on the wrapper, for styling one instance differently.
[products limit="12" columns="3" paginate="true" orderby="price" order="ASC"]
orderby="rand" is worth a warning: it bypasses query caching and is slow on a large catalogue, because the database cannot reuse a result set that must be different each time.
Categories, single products, and buttons
[product_categories limit="6" columns="3" parent="0" hide_empty="1"]
[product_categories ids="12,34"]
[product_category category="hoodies" limit="8" columns="4"]
[product_page id="99"]
[product_page sku="SHIRT-01"]
[add_to_cart id="99"]
[add_to_cart id="99" show_price="false" style="" quantity="2"]
[add_to_cart_url id="99"]
[shop_messages]
[product_page]renders a complete single-product page, including the gallery and add-to-cart form, inside another page. Useful for a landing page built around one product.[add_to_cart]is a single button with price.show_price="false"drops the price, andstyle=""removes the inline border WooCommerce adds by default.[add_to_cart_url]outputs just the URL, for putting the action behind your own markup.[shop_messages]displays WooCommerce notices — “added to cart”, errors — on pages that are not WooCommerce pages. Needed if you put an add-to-cart button on a normal page and want the confirmation to appear.
That last one is the fix for “the button works but nothing happens visually” on a custom landing page. The product goes in the cart; there is just nowhere for the notice to render.
Blocks, and when to migrate
WooCommerce’s newer development is in blocks, and the Cart and Checkout blocks are substantially different from the shortcodes rather than a cosmetic reskin. They are React-based, they use the Store API, and they have their own extensibility model.
Move to blocks when:
- The store is new, so there is nothing to migrate.
- Checkout customisation is minimal or none.
- You want the newer checkout features, which are block-only.
- Your extensions declare block compatibility.
Stay on shortcodes when:
- You have custom checkout fields added via the classic hooks — those do not carry over.
- A payment gateway or shipping extension has not added block support.
- The checkout has significant custom PHP behaviour built on
woocommerce_checkout_*actions. - You are mid-season and cannot risk the checkout. Migrate in a quiet period.
The migration risk is concentrated entirely in the checkout, because it is the page where a subtle break costs money immediately and silently. Test it end to end with a real payment method in a staging copy, not by looking at it.
Shortcodes in templates and blocks
<?php
// Render a shortcode from PHP
echo do_shortcode('[products limit="4" columns="4"]');
// Register your own
add_shortcode('featured_deal', function ($atts) {
$a = shortcode_atts(['id' => 0], $atts);
if (!$a['id']) {
return '';
}
return do_shortcode(sprintf('[product_page id="%d"]', (int) $a['id']));
});
shortcode_atts merges user attributes over your defaults, so missing attributes do not produce notices. Cast and validate anything that reaches a query — a shortcode attribute is user input when the content is editable by more than one person.
In the block editor, use a Shortcode block or paste the shortcode into a paragraph. In widget areas and template parts it needs do_shortcode, since not every context runs shortcodes automatically.
If a shortcode appears as literal text on the page, it is not registered — usually because the plugin providing it is deactivated, or it is in a context that does not process shortcodes. Both are easy to confirm and neither is a WooCommerce bug.
Performance
Product shortcodes run database queries. Several on one page, each unbounded, is a slow page.
- Always set
limit.limit="-1"on a catalogue of thousands loads all of them into memory. - Use
paginate="true"rather than a high limit for browsable listings. - Avoid
orderby="rand"on anything large — it defeats query caching. - Prefer
idsorskuswhen showing a specific set; it is a much cheaper query than filtering by attribute. - Cache the page. WooCommerce pages are trickier to cache than a blog, but a product listing page usually can be.
The caching caveat matters: never cache cart, checkout, or account pages. They are per-user, and a cached checkout can serve one customer another customer’s session. Most WooCommerce-aware caching plugins exclude these automatically, but a hand-rolled nginx or CDN rule will not unless you tell it to.
WooCommerce is heavier than plain WordPress — more tables, more queries per page, more work at checkout — so a store that outgrew its plan shows up as slow product pages long before anything actually fails. Plans with defined vCPU and RAM, and autoscaling between a floor and a ceiling for seasonal traffic, is what stops a sale day being the thing that finds the limit.
How this fits the rest of the stack
[woocommerce_cart], [woocommerce_checkout], [woocommerce_my_account], and [woocommerce_order_tracking] for the core pages, [products] with limit, columns, orderby, and paginate for listings, and [product_page] or [add_to_cart] for individual products. Add [shop_messages] when putting cart actions on a non-WooCommerce page.
Move to the Cart and Checkout blocks for new stores and stay on shortcodes if your checkout has custom fields or extensions without block support. Always set limit, never cache cart or checkout. If you are working out what the store costs to run at peak, the RunxBuild hosting calculator shows plan, storage, and bandwidth separately.
Useful related references:
FAQ
What are the main WooCommerce shortcodes?
[woocommerce_cart], [woocommerce_checkout], [woocommerce_my_account], and [woocommerce_order_tracking] for the four core pages, plus [products] for listings, [product_page] for a full single product, [product_categories] for category grids, and [add_to_cart] for a single button.
How do I display products with a shortcode in WooCommerce?
Use [products] with attributes such as limit, columns, orderby, order, and paginate. You can filter by category, tag, ids, skus, or attribute and terms, or use flags like on_sale="true" and best_selling="true". Always set a limit rather than loading the whole catalogue.
Should I use WooCommerce blocks or shortcodes?
Blocks for new stores with little checkout customisation, since that is where WooCommerce’s development is focused. Stay on shortcodes if you have custom checkout fields added through classic hooks or extensions without block support, because those do not carry across and the checkout is the worst place to discover a gap.
Why is my WooCommerce shortcode showing as plain text?
The shortcode is not registered in that context — usually the plugin providing it is deactivated, or the content is being rendered somewhere that does not process shortcodes. In template files and some widget areas you need do_shortcode() explicitly rather than relying on automatic processing.
Can I cache pages that use WooCommerce shortcodes?
Product listing pages generally yes. Never cache cart, checkout, or my-account pages — they are per-user, and a cached checkout can show one customer another’s session. WooCommerce-aware caching plugins exclude these automatically, but custom nginx or CDN rules will not unless configured to.