woocommerce recently viewed products

How to Show Recently Viewed Products in WooCommerce

A lot of store owners assume WooCommerce needs a paid plugin to show a “recently viewed” section. It doesn’t – WooCommerce already tracks this in the background and ships with a built-in widget for it. The confusion comes from wanting more control than that widget gives you: placing the list somewhere other than a sidebar, like the homepage or the cart page.

Here’s both the quick native option and a custom shortcode for when you need it placed somewhere specific.

Option 1: The Native Widget (No Code Needed)

WooCommerce already tracks which products a visitor has viewed, cookie by cookie, without any setup on your part. To display that list:

  • Go to Appearance > Widgets
  • Add the Recently Viewed Products widget to any sidebar or widget area
  • If you’re using a block theme, search for the equivalent block in the block editor instead

This works immediately for most stores. The limitation is that it’s tied to widget areas – if your theme doesn’t have a sidebar on the page you want it, or you want it inline within page content rather than in a sidebar, you’ll need the shortcode approach below.

Option 2: A Custom Shortcode (Place It Anywhere)

This tracks viewed products independently and gives you a shortcode you can drop into any page, post, or template – the homepage, the cart page, wherever you want.

Step 1 – Track viewed products in a cookie

/**
 * Track viewed products in a cookie whenever a single product page is visited.
 */
add_action( 'woocommerce_before_single_product', 'wpcodex_track_viewed_product' );
function wpcodex_track_viewed_product() {

    global $product;

    if ( ! $product ) {
        return;
    }

    $max_items = 10;
    $viewed    = isset( $_COOKIE['wpcodex_viewed_products'] ) ? (array) json_decode( stripslashes( $_COOKIE['wpcodex_viewed_products'] ) ) : array();

    $product_id = $product->get_id();

    // Remove if already in the list, then add it to the front
    $viewed = array_diff( $viewed, array( $product_id ) );
    array_unshift( $viewed, $product_id );

    $viewed = array_slice( $viewed, 0, $max_items );

    wc_setcookie( 'wpcodex_viewed_products', json_encode( $viewed ), time() + ( 30 * DAY_IN_SECONDS ) );
}

Step 2 – Add the shortcode to display them

/**
 * Shortcode to display recently viewed products: [wpcodex_recently_viewed]
 */
add_shortcode( 'wpcodex_recently_viewed', 'wpcodex_recently_viewed_shortcode' );
function wpcodex_recently_viewed_shortcode( $atts ) {

    $atts = shortcode_atts( array(
        'limit' => 4,
    ), $atts );

    if ( empty( $_COOKIE['wpcodex_viewed_products'] ) ) {
        return '';
    }

    $viewed_ids = array_map( 'absint', (array) json_decode( stripslashes( $_COOKIE['wpcodex_viewed_products'] ) ) );

    // Don't show the product currently being viewed, if any
    if ( is_product() ) {
        global $product;
        $viewed_ids = array_diff( $viewed_ids, array( $product->get_id() ) );
    }

    $viewed_ids = array_slice( $viewed_ids, 0, absint( $atts['limit'] ) );

    if ( empty( $viewed_ids ) ) {
        return '';
    }

    $ids_string = implode( ',', $viewed_ids );

    return do_shortcode( '[products ids="' . $ids_string . '" orderby="post__in"]' );
}

Use it by placing [wpcodex_recently_viewed] in any page or post, or inside a template file with echo do_shortcode( '[wpcodex_recently_viewed limit="4"]' );.

The shortcode reuses WooCommerce’s own core [products] shortcode behind the scenes, so the output styling automatically matches the rest of your store’s product grid without any extra CSS.

Where to Add This Code

  • Your child theme’s functions.php, or
  • A site-specific plugin (recommended – it survives theme updates), or
  • A code snippets plugin like WPCode or Code Snippets

Troubleshooting

The list is empty even after viewing several products.
Check whether your hosting or a caching plugin is stripping cookies on page load. Some aggressive full-page caching setups don’t allow cookies to be set correctly unless the product pages are excluded from the cache, since a cached page can’t run the PHP that sets the cookie.

It shows the same products for every visitor.
This usually means the cookie isn’t being scoped per-browser correctly, often caused by a caching layer serving an identical cached version of a page to everyone. If you’re using a full-page cache, confirm dynamic, cookie-based content like this is allowed to bypass it.

Can I show this to logged-in users across devices, not just per-browser?
Not with this snippet as written, since it relies on browser cookies rather than user accounts. To track it per logged-in user account instead, you’d need to store the viewed list in user meta rather than a cookie, keyed to the logged-in user ID.

FAQ

Does this slow down my product pages?
No. Reading and writing a small cookie is a negligible operation compared to a full product page load.

Will this conflict with the native WooCommerce widget?
No, they’re independent of each other. You can use the native widget in a sidebar and the custom shortcode elsewhere on the same site without any conflict, since they use separate cookies.

Can I exclude out-of-stock products from the recently viewed list?
Yes – add a stock status check inside the shortcode function before including a product ID in the final output, so any product that’s gone out of stock since it was viewed won’t be shown.