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.

