Sticky Add-to-Cart Bar
A bar that slides up when the buy button scrolls off screen, with price and variant.
Shows each shopper the products they looked at, so they can get back to them in a tap.
Important: Before making any changes to your theme, duplicate it in Online Store > Themes so you have a backup.
Most shoppers look at several products before they buy one. Recently viewed products saves them hunting through menus to get back to the one they liked. Views are remembered in the shopper's browser, so it needs no app and no account.
Recently viewed
Wool Beanie
£22.00
Leather Gloves
£45.00
Cashmere Scarf
£79.00
Rain Jacket
£120.00
Preview with the default settings. Colours, text and layout can all be changed in the theme editor.
In Shopify admin go to Online Store > Themes. Next to the theme you want to edit, click the three dots and choose Edit code.
In the Sections folder click Add a new section, name it lt-recently-viewed-products, delete the placeholder code, paste in the code below and click Save.
Click Customize, go to Products > Default product, click Add section and choose Recently viewed.
Change the settings in the sidebar, drag the section where you want it, and click Save.
{%- comment -%}
Recently viewed products by LAUNCHTIP
https://launchtip.com/free-shopify-sections-code
Add it to the product template so views are recorded. You can also add it
to the home, collection or cart templates to show the same list there.
{%- endcomment -%}
{%- style -%}
#lt-{{ section.id }} { padding: {{ section.settings.padding }}px 0; }
#lt-{{ section.id }}[hidden] { display: none; }
#lt-{{ section.id }} .lt-rv__heading { margin: 0 0 16px; }
#lt-{{ section.id }} .lt-rv__grid { display: grid; gap: 16px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 750px) { #lt-{{ section.id }} .lt-rv__grid { grid-template-columns: repeat({{ section.settings.columns }}, minmax(0, 1fr)); } }
#lt-{{ section.id }} .lt-rv__card { color: inherit; text-decoration: none; display: block; }
#lt-{{ section.id }} .lt-rv__media { aspect-ratio: {{ section.settings.ratio }}; background: #f5f5f5; border-radius: 8px; overflow: hidden; }
#lt-{{ section.id }} .lt-rv__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
#lt-{{ section.id }} .lt-rv__title { margin: 10px 0 2px; font-size: 0.95em; }
#lt-{{ section.id }} .lt-rv__price { font-size: 0.9em; opacity: 0.8; }
#lt-{{ section.id }} .lt-rv__price s { opacity: 0.6; margin-left: 6px; }
{%- endstyle -%}
<div
id="lt-{{ section.id }}"
class="page-width"
data-current="{% if template.name == 'product' %}{{ product.handle }}{% endif %}"
data-limit="{{ section.settings.limit }}"
data-root="{{ routes.root_url }}"
data-show-price="{{ section.settings.show_price }}"
data-currency="{{ cart.currency.iso_code }}"
hidden
>
{%- if section.settings.heading != blank -%}
<h2 class="lt-rv__heading h2">{{ section.settings.heading }}</h2>
{%- endif -%}
<div class="lt-rv__grid"></div>
</div>
<script>
(function () {
var root = document.getElementById('lt-{{ section.id }}');
if (!root || root.dataset.ready) return;
root.dataset.ready = '1';
var KEY = 'lt_recently_viewed';
var current = root.dataset.current;
var limit = Number(root.dataset.limit);
var grid = root.querySelector('.lt-rv__grid');
var base = root.dataset.root.replace(/\/$/, '');
function read() {
try { return JSON.parse(localStorage.getItem(KEY)) || []; } catch (e) { return []; }
}
var handles = read();
if (current) {
handles = [current].concat(handles.filter(function (h) { return h !== current; })).slice(0, 20);
try { localStorage.setItem(KEY, JSON.stringify(handles)); } catch (e) {}
}
var toShow = handles.filter(function (h) { return h !== current; }).slice(0, limit);
if (!toShow.length) return;
function money(cents) {
var currency = (window.Shopify && Shopify.currency && Shopify.currency.active) || root.dataset.currency;
return new Intl.NumberFormat(document.documentElement.lang || undefined, { style: 'currency', currency: currency }).format(cents / 100);
}
function sized(src, width) {
if (!src) return '';
return src + (src.indexOf('?') > -1 ? '&' : '?') + 'width=' + width;
}
function card(product) {
var link = document.createElement('a');
link.className = 'lt-rv__card';
link.href = product.url;
var media = document.createElement('div');
media.className = 'lt-rv__media';
if (product.featured_image) {
var img = document.createElement('img');
img.src = sized(product.featured_image, 480);
img.alt = product.title;
img.loading = 'lazy';
media.appendChild(img);
}
link.appendChild(media);
var title = document.createElement('p');
title.className = 'lt-rv__title';
title.textContent = product.title;
link.appendChild(title);
if (root.dataset.showPrice === 'true') {
var price = document.createElement('span');
price.className = 'lt-rv__price';
price.textContent = money(product.price);
if (product.compare_at_price > product.price) {
var was = document.createElement('s');
was.textContent = money(product.compare_at_price);
price.appendChild(was);
}
link.appendChild(price);
}
return link;
}
Promise.all(toShow.map(function (handle) {
return fetch(base + '/products/' + encodeURIComponent(handle) + '.js')
.then(function (r) { return r.ok ? r.json() : null; })
.catch(function () { return null; });
})).then(function (products) {
var found = products.filter(Boolean);
if (!found.length) return;
var stillThere = found.map(function (p) { return p.handle; });
localStorage.setItem(KEY, JSON.stringify(read().filter(function (h) { return h === current || stillThere.indexOf(h) > -1; })));
found.forEach(function (product) { grid.appendChild(card(product)); });
root.hidden = false;
});
})();
</script>
{% schema %}
{
"name": "Recently viewed",
"tag": "section",
"class": "lt-rv-section",
"settings": [
{
"type": "text",
"id": "heading",
"label": "Heading",
"default": "Recently viewed"
},
{
"type": "range",
"id": "limit",
"label": "Products to show",
"min": 2,
"max": 12,
"step": 1,
"default": 4
},
{
"type": "range",
"id": "columns",
"label": "Columns on desktop",
"min": 2,
"max": 6,
"step": 1,
"default": 4
},
{
"type": "select",
"id": "ratio",
"label": "Image shape",
"options": [
{ "value": "1 / 1", "label": "Square" },
{ "value": "3 / 4", "label": "Portrait" },
{ "value": "4 / 3", "label": "Landscape" }
],
"default": "1 / 1"
},
{
"type": "checkbox",
"id": "show_price",
"label": "Show price",
"default": true
},
{
"type": "range",
"id": "padding",
"label": "Vertical padding",
"min": 0,
"max": 100,
"step": 4,
"unit": "px",
"default": 40
}
],
"presets": [
{
"name": "Recently viewed"
}
]
}
{% endschema %}
| Setting | Default |
|---|---|
| Heading | Recently viewed |
| Products to show | 4 |
| Columns on desktop | 4 |
| Image shape | Square |
| Show price | On |
| Vertical padding | 40px |
In the shopper's own browser (localStorage). Nothing is sent to a server and no cookie banner is needed for it.
No. Each browser keeps its own list.
It stays hidden until the shopper has viewed at least one other product.
A bar that slides up when the buy button scrolls off screen, with price and variant.
Text and images that change when the shopper picks a different colour or size.
Your latest blog posts in a clean grid with images, dates and excerpts.
Discover more code snippets to enhance your Shopify store.
View All Sections