Facebook iconHow to Add a Recently Viewed Products Section to Your Shopify Store?
Blogs/Shopify

How to Add a Recently Viewed Products Section to Your Shopify Store?

May 9, 20256 Min Read
Written by Sahil Singh
How to Add a Recently Viewed Products Section to Your Shopify Store? Hero

Have you ever wished your online store could remember what products your customers were interested in? A Shopify recently viewed products section might be exactly what your Shopify store needs.

This article will guide you through implementing this powerful feature that tracks and displays items customers have previously browsed, creating a personalized shopping experience that can significantly boost your conversion rates.

You'll learn about the key benefits of this feature, the technical components behind it, and a step-by-step implementation guide complete with code snippets. By the end, you'll understand how this simple addition can enhance user experience, reduce cart abandonment, and create natural opportunities for cross-selling. Ready to give your customers a more intuitive shopping experience? Let's dive in!

What Are Recently Viewed Products?

The Shopify recently viewed products feature offers numerous advantages for store owners looking to optimize their customer experience. By implementing this solution, you'll create multiple touchpoints that guide customers back to products they've shown interest in.

Using browser cookies and local storage, this feature captures the products a customer has shown interest in, making it easy for them to find and revisit those items.

Benefits of Recently Viewed Products for Your Shopify Store

1. Enhanced User Experience

Imagine walking into a physical store where an assistant remembers exactly what caught your eye. That's precisely what Recently Viewed Products does online. It makes it easy for customers to rediscover items they were interested in especially ones they may have forgotten during their browsing journey.

2. Increased Conversion Rates

Customers are more likely to purchase products they've already shown interest in. Through strategic conversion optimization and repeated presentations of these items, you create multiple touchpoints that can nudge a hesitant shopper toward making a purchase.

3. Personalisation at Its Best

In an era of generic online shopping, recently viewed products add a layer of individualised experience, making customers feel understood and valued.

4. Reduced Cart Abandonment

Sometimes customers get distracted or need time to make a decision. This feature acts as a gentle reminder, bringing those potential purchases back into focus.

5. Cross-Selling and Upselling Opportunities

By showcasing previously viewed items, you create natural opportunities to suggest complementary or upgraded products, potentially increasing the average order value.

Partner with Us for Success

Experience seamless collaboration and exceptional results.

The Core Components of this Implementation

1. Product Tracking

The key technical features:

  • 7-Day Product Expiration: Products automatically expire after a week, keeping the recommendations fresh and relevant
  • Performance-Optimized: Lightweight JavaScript implementation
  • Conditional Rendering: Section hidden when no products are available

2. Key Technical Highlights

LocalStorage Management

class RecentlyViewedProducts {
  constructor() {
    this.storageKey = 'oss_recently_viewed';
    this.maxProducts = 4; // Configurable product limit
    this.expirationDays = 7; // Automatic data expiration
  }
}

This constructor sets up the core parameters for tracking recently viewed products, demonstrating a clean, modular approach to managing product history.

3. Product Cleanup

cleanExpiredProducts() {
  const products = this.getStoredProducts();
  const validProducts = products.filter(product => 
    !this.isProductExpired(product) && 
    product.published_at
  );
}

The cleanExpiredProducts() method ensures that:

  • Only current, published products are stored
  • Old products are automatically removed
  • The product list remains fresh and relevant

4. Performance and User Experience

renderProducts() {
  // Clean expired products before rendering
  this.cleanExpiredProducts();
  
  const products = this.getStoredProducts();
  
  // Hide section if no products
  if (!products || products.length === 0) {
    this.container.style.display = 'none';
    return;
  }
}

Key user experience features:

  • Automatic section visibility management
  • Efficient product rendering
  • Graceful handling of empty product lists

5. Customization Options

  • Adjust maximum number of products
  • Modify expiration period
  • Customize styling to match store design

How to Add a Recently Viewed Products Section in Shopify: Step-by-Step Guide

Step 1: Create the Liquid Section

Create the Liquid Section in Shopify to create recently viewed products
{% comment %}
  Recently Viewed Products Section
  - Only shows published/active products
  - Data expires after 7 days
  - Hidden by default until products are available
{% endcomment %}

<style>
  .oss-recently-viewed {
    margin-bottom: 2rem;
    display: none;
  }

  .oss-recently-viewed.has-products {
    display: block;
  }

  .oss-recently-title h2 {
    font-size: 35pt !important;
    margin-bottom: 1rem;
    padding-top: 1rem;
  }

  .oss-product-title{
    font-family: "Moniqa-Condensed";
    font-size: 17pt;
    color: black;
    text-transform: uppercase;
  }

  .oss-product-price{
    color: black;
    font-family: "Gill Sans";
    font-size: 11pt;
    font-weight: normal;
  }

  .oss-recent-products-block {
    display: grid;
    gap: var(--grid-desktop-horizontal-spacing, 20px);
  }

  @media only screen and (min-width: 750px) { 
    .oss-recent-products-block {
      grid-template-columns: repeat(4, 1fr);
    }
  }

  @media only screen and (max-width: 749px) {
    .oss-recent-products-block {
      grid-template-columns: repeat(2, 1fr);
    }

    .oss-product-title{
      font-size: 11.5pt;
    }

    .oss-product-price{
      font-size: 9.5pt;
    }
  }

  .oss-product {
    cursor: pointer;
  }

  .oss-product-img img {
    aspect-ratio: 3.2/5;
    object-fit: cover;
  }

  .oss-product-title {
    margin: 10px 0 5px;
  }

  .oss-product-title a {
    color: black;
    text-decoration: none;
  }

  .oss-product-price {
    margin: 0;
    color: #000;
  } 
</style>

{% assign current_time = 'now' | date: '%s' | times: 1000 %}

{% if template contains 'product' and product.published_at != nil %}
  <script type="application/json" id="CurrentProductData">
    {
      "id": {{ product.id | json }},
      "title": {{ product.title | json }},
      "handle": {{ product.handle | json }},
      "url": {{ product.url | json }},
      "featured_image": "{{ product.featured_image | img_url: 'master' }}",
      "price": {{ product.price | money | json }},
      "timestamp": {{ current_time | json }},
      "published_at": {{ product.published_at | date: '%s' | times: 1000 | json }}
    }
  </script>
{% endif %}

<section class="oss-recently-viewed page-width{% if section.settings.full_width %} page-width-desktop{% endif %}" id="RecentlyViewed-{{ section.id }}">
  {% if section.settings.title != blank %}
    <div class="oss-recently-title">
      <h2 class="title inline-richtext h2 scroll-trigger animate--slide-in">{{ section.settings.title }}</h2>
    </div>
  {% endif %}
  <div class="oss-recent-products-block">
  </div>
</section>

<script>
  class RecentlyViewedProducts {
    constructor() {
      this.storageKey = 'oss_recently_viewed';
      this.maxProducts = {{ section.settings.products_to_show }};
      this.expirationDays = 7; // Data expires after 7 days
      this.container = document.querySelector('#RecentlyViewed-{{ section.id }}');
      this.productsContainer = this.container.querySelector('.oss-recent-products-block');
    }

    init() {
      this.cleanExpiredProducts();
      this.renderProducts();
      this.updateCurrentProduct();
    }

    getCurrentProduct() {
      const dataElement = document.getElementById('CurrentProductData');
      if (!dataElement) return null;

      try {
        const productData = JSON.parse(dataElement.textContent);
        if (!productData || !productData.url || !productData.published_at) return null;
        return productData;
      } catch (error) {
        console.error('Error parsing current product data:', error);
        return null;
      }
    }

    isProductExpired(product) {
      if (!product.timestamp) return true;
      
      const now = new Date().getTime();
      const age = now - product.timestamp;
      const expirationTime = this.expirationDays * 24 * 60 * 60 * 1000; // Convert days to milliseconds
      
      return age > expirationTime;
    }

    cleanExpiredProducts() {
      const products = this.getStoredProducts();
      const validProducts = products.filter(product => 
        !this.isProductExpired(product) && 
        product.published_at
      );
      
      if (validProducts.length !== products.length) {
        this.setStoredProducts(validProducts);
      }
    }

    getStoredProducts() {
      try {
        const stored = localStorage.getItem(this.storageKey);
        return stored ? JSON.parse(stored) : [];
      } catch (error) {
        console.error('Error reading from localStorage:', error);
        return [];
      }
    }

    setStoredProducts(products) {
      try {
        // Filter out any products that are expired or unpublished
        const validProducts = products.filter(product => 
          !this.isProductExpired(product) && 
          product.published_at
        );
        localStorage.setItem(this.storageKey, JSON.stringify(validProducts));
      } catch (error) {
        console.error('Error writing to localStorage:', error);
      }
    }

    updateCurrentProduct() {
      const currentProduct = this.getCurrentProduct();
      if (!currentProduct) return;

      let products = this.getStoredProducts();

      // Remove if already exists
      products = products.filter(product => product.url !== currentProduct.url);

      // Add to start of array
      products.unshift(currentProduct);

      // Limit to max products
      products = products.slice(0, this.maxProducts);

      this.setStoredProducts(products);
      this.renderProducts();
    }

    renderProducts() {
      if (!this.productsContainer) return;

      // Clean expired products before rendering
      this.cleanExpiredProducts();
      
      const products = this.getStoredProducts();
      
      // If no products, hide the entire section and return early
      if (!products || products.length === 0) {
        this.container.style.display = 'none';
        return;
      }
      
      // Show section if we have products
      this.container.style.display = 'block';
      
      // Toggle section visibility
      this.container.classList.toggle('has-products', products.length > 0);
      
      const html = products.map(product => {
        // Validate required fields
        if (!product.url || !product.title || !product.featured_image || !product.published_at) return '';
        
        return `
          <div class="oss-product">
            <div class="oss-product-img">
              <a href="${product.url}">
                <img src="${product.featured_image}" alt="${product.title}" loading="lazy"/>
              </a>
            </div>
            <p class="oss-product-title">
              <a href="${product.url}">${product.title}</a>
            </p>
            <p class="oss-product-price">${product.price}</p>
          </div>
        `;
      }).join('');

      this.productsContainer.innerHTML = html;
    }
  }

  // Initialize on DOM content loaded
  document.addEventListener('DOMContentLoaded', () => {
    const recentlyViewed = new RecentlyViewedProducts();
    recentlyViewed.init();
  });
</script>

{% schema %}
{
  "name": "Recently Viewed Products",
  "tag": "section",
  "class": "section",
  "settings": [
    {
      "type": "text",
      "id": "title",
      "default": "Recently Viewed",
      "label": "Title"
    },
    {
      "type": "range",
      "id": "products_to_show",
      "min": 2,
      "max": 8,
      "step": 1,
      "default": 4,
      "label": "Maximum products to show"
    },
    {
      "type": "checkbox",
      "id": "full_width",
      "default": false,
      "label": "Full width"
    }
  ],
  "presets": [
    {
      "name": "Recently Viewed Products"
    }
  ]
}
{% endschema %}

Step 2: Add Section to Theme

Add section to theme in Shopify

Conclusion

This Recently Viewed Products feature helps shoppers find items they've already looked at. It's easy to add to your Shopify store and makes shopping more personal.

Partner with Us for Success

Experience seamless collaboration and exceptional results.

The code keeps track of what customers view and shows these products in a neat section. This helps customers remember products they liked but forgot to buy.

Store owners can easily change how many products show up and how the section looks.

Adding this feature is a simple way to improve your store and help customers find what they want, which can lead to more sales.

Need Expert Help?

Want to implement a customized recently viewed products section but don't have technical expertise? Hire Shopify experts at F22 Labs to create a tailored solution for your store. Our specialists can optimize your product tracking, enhance the visual design, and ensure seamless integration with your existing theme boosting engagement and conversions.

Author-Sahil Singh
Sahil Singh

I’m a Front-End developer with 1.3 years of experience creating user-friendly, optimized web pages. I specialize in developing features that boost conversion rates for e-commerce websites.

Phone

Next for you

A Guide To Shopify Store Speed Optimisation in 2025 Cover

Shopify

Jun 4, 20255 min read

A Guide To Shopify Store Speed Optimisation in 2025

Google data shows 53% of mobile users abandon sites that take over 3 seconds to load, and a 1-second delay can slash conversions by 20%. For Shopify store owners, those numbers hit hard. But here’s the good news: you don’t need a big team or deep pockets to make your store blazing fast. With smart tools, a lean approach, and a clear plan, you can turn your Shopify store into a high-speed sales machine. Ready to make your store unstoppable? This Shopify store speed optimisation guide is your ul

12 Essential Questions to Ask Before Hiring a Shopify Developer Cover

Shopify

Jun 3, 20257 min read

12 Essential Questions to Ask Before Hiring a Shopify Developer

Pick the wrong Shopify developer, and you’re stuck with a glitchy site, missed deadlines, and a drained budget. Choose the right one, and your store becomes a fast, stunning sales machine.  Finding the perfect developer doesn’t have to feel like a gamble. You just need the right questions to separate the pros from the pretenders. In this article, you will learn the 12 essential questions before hiring Shopify developers and ensure your store is built to make money, so you can focus on growing y

How To Turn Shopify Store Traffic to Sales in 8 Steps in (2025) Cover

Shopify

Jun 3, 20256 min read

How To Turn Shopify Store Traffic to Sales in 8 Steps in (2025)

Clicks are rolling in, analytics dashboards are glowing, and you’re ready to watch sales stack up. But at the end of the day? Crickets. No orders, no revenue, just a sinking feeling that something’s broken.  Your traffic isn’t converting, and every missed sale feels like a punch. In 2025, traffic without sales isn’t just frustrating, it’s a sign your store’s letting customers slip through the cracks. Conversions are your store’s lifeblood.  If you’re getting 1,000 visitors a month, that’s the