header blog

WooCommerce Product Shortcode: Show Products Anywhere on Your Site

The [products] shortcode is the single most flexible shortcode WooCommerce ships with. It replaced a handful of older, single-purpose shortcodes ([featured_products], [sale_products], [best_selling_products], and others), and it can filter by category, tag, attribute, SKU, or sale status in one call.

The Code

Here are a few ready-to-copy variations covering the most common needs:

[products limit="8" columns="4" category="hoodies"]

Shows 8 products from the “hoodies” category, in a 4-column grid.

[products limit="4" columns="4" on_sale="true" orderby="popularity"]

Shows 4 products currently on sale, sorted by popularity.

[products limit="4" columns="4" visibility="featured"]

Shows products marked as Featured in the catalog.

[products limit="3" columns="3" best_selling="true"]

Shows your 3 best-selling products.

[products limit="6" columns="3" tag="new-arrival" orderby="date" order="DESC"]

Shows the newest products tagged “new-arrival.”

Where to Put This Code

This shortcode goes directly into a page or post’s content, not into functions.php. In the block editor, use a Shortcode block rather than a Paragraph block, since some setups render the shortcode as plain text instead of executing it inside a Paragraph block.

If you need to sort products by a custom meta field, like a value coming from a plugin that isn’t one of WooCommerce’s default orderby options, that requires a small filter in your child theme’s functions.php:

add_filter( 'woocommerce_shortcode_products_query', 'fixelar_products_shortcode_orderby' );

function fixelar_products_shortcode_orderby( $args ) {
    $standard_array = array( 'menu_order', 'title', 'date', 'rand', 'id' );

    if ( isset( $args['orderby'] ) && ! in_array( $args['orderby'], $standard_array, true ) ) {
        $args['meta_key'] = $args['orderby'];
        $args['orderby']  = 'meta_value_num';
    }

    return $args;
}

With this in place, passing orderby="_custom_field_name" in the shortcode sorts by that meta field numerically.

Parameters, Explained

Display attributes control layout and order: limit (how many to show, -1 for all), columns (grid width, default 4), paginate (true/false), orderby (title, date, id, menu_order, popularity, rand, rating), and order (ASC or DESC).

Content attributes control which products show up: category and tag accept comma-separated slugs, skus accepts comma-separated SKUs, ids accepts comma-separated post IDs, and visibility filters by catalog visibility (visible, catalog, search, hidden, featured).

Operators control how multiple values combine: cat_operator, tag_operator, and terms_operator each accept IN (default, match any), NOT IN (exclude), or AND (match all). category="hoodies, tshirts" cat_operator="AND" only shows products that are in both categories at once, which for most catalogs returns very few or zero results, since most products only belong to one.

Special attributes, on_sale and best_selling, are mutually exclusive with each other and with visibility="featured". Combining them doesn’t error out, but the results become unpredictable, so pick one filtering strategy per shortcode instance.

One easy mistake: category and tag expect the slug, not the display name. A category named “Summer Sale” typically has the slug summer-sale, and passing the display name instead returns an empty grid.

Real Use Cases

A homepage section for a specific collection. [products category="new-arrivals" limit="8" columns="4"] builds a curated section without a page builder plugin or a custom loop.

A sale banner page during a promotion. [products on_sale="true" limit="12" columns="4"] automatically pulls whatever’s currently on sale, so the page stays accurate without manual updates when a sale ends or a new one starts.

Cross-selling inside a blog post. Embedding [products tag="gift-guide" limit="4"] inside an editorial post turns a buying guide into something people can shop directly from.

A landing page built around one product attribute. For stores with meaningful custom attributes (season, material, use case), [products attribute="season" terms="warm"] builds a filtered grid without a dedicated attribute archive template.

Common Issues

The shortcode shows nothing. Check that category, tag, or skus values are actual slugs or SKUs that exist, and check the product’s Catalog Visibility setting isn’t set to Hidden.

Combining category with cat_operator=”AND” returns almost nothing. This is expected behavior, not a bug. AND requires a product to be in every listed category simultaneously. Use IN (the default) if you want products from any of the listed categories.

Sorting by orderby=”rand” looks fine at first, then stops changing. Full-page caching plugins can save the first randomized output and keep serving it. Random ordering and full-page caching don’t mix well; if the store uses aggressive caching, rand won’t behave as expected for repeat visitors.

Custom orderby values don’t work at all. WooCommerce’s built-in orderby options are limited to the standard list (title, date, id, menu_order, popularity, rand, rating). Anything else, like sorting by a custom price-per-unit meta field, needs the woocommerce_shortcode_products_query filter shown above.

If you need product grids driven by logic more complex than what shortcode attributes can express, custom sorting, multi-condition filtering, or dynamic collections based on customer data, that’s exactly the kind of work Fixelar handles as part of our WooCommerce custom development services. We build the query logic once and make sure it stays fast as your catalog grows.