Implementing infinite scroll on your Shopify collection pages replaces clunky pagination with a fluid, uninterrupted browse keeping shoppers engaged longer and guiding them smoothly toward purchase.
User Experience
• Removes “Next” button roadblocks, loading new products automatically as customers reach the page bottom
• Mimics popular social-media feeds, creating a familiar, swipe friendly flow especially on mobile
• Reduces cognitive load; visitors never decide which page to click, they just keep scrolling
• Preserves search or filter selections while adding products in real time, preventing context loss
Engagement & Dwell Time
• Encourages deeper exploration as curiosity drives shoppers to uncover what appears next
• Increases session length, boosting the likelihood of product discovery and add-to-cart actions
• Lowers bounce rates; momentum builds naturally instead of resetting at each pagination break
• Provides a modern, dynamic feel that reflects a tech-forward, customer-focused brand
Conversion Lift
• Surfaces more SKUs without extra clicks, improving odds that each visitor finds an item they love
• Supports impulse buys when sale or best-seller items pop up farther down the feed
• Combines seamlessly with sticky add to cart bars or quick-add buttons for frictionless purchasing
• Enables strategic “load more” checkpoints where promotional banners or upsells can appear mid-scroll
Operational & SEO Considerations
• Streamlines analytics fewer pageviews but richer engagement metrics like scroll depth and time on page
• Can be configured for SEO friendliness with “load on link click” fallback or paginated URLs for crawlers
• Reduces server load spikes from rapid multi page requests, fetching products in controlled batches
• Plays well with lazy-loaded images via Shopify’s CDN, safeguarding Core Web Vitals
CODE : Snippet/infinite-scroll.liquid
{% comment %}
<!-- Designed by OnHOW YouTube Channel - Anas El Medlaoui -->
{% endcomment %}
<infinite-scroll data-next="{{ next_page }}" data-page-size="{{ page_size }}" data-end-text="{{ end_text }}">
{% if page_size > 1 %}
<div class="onhow-loading-container">
<span id="onhow-loading-1" class="onhow-loading-dot"> </span>
<span id="onhow-loading-2" class="onhow-loading-dot"> </span>
<span id="onhow-loading-3" class="onhow-loading-dot"> </span>
<span id="onhow-loading-4" class="onhow-loading-dot"> </span>
<span id="onhow-loading-5" class="onhow-loading-dot"> </span>
<span id="onhow-loading-6" class="onhow-loading-dot"> </span>
<span id="onhow-loading-7" class="onhow-loading-dot"> </span>
<span id="onhow-loading-8" class="onhow-loading-dot"> </span>
</div>
{% endif %}
</infinite-scroll>
<style>
infinite-scroll {
position: relative;
width: 100%;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.5s ease;
margin: 2rem 0;
}
.onhow-loading-container {
position: relative;
width: 234px;
height: 28px;
margin: auto;
}
.onhow-loading-dot {
position: absolute;
top: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
width: 28px;
height: 28px;
border-radius: 50%;
animation: onhow-loading-wave 1.5s infinite ease-in-out;
transform: scale(0.3);
}
.onhow-end-section {
text-align: center;
padding: 3rem 1rem 2rem;
font-family: var(--font-body-family);
}
.onhow-end-text {
font-size: 2.25rem;
font-weight: 500;
color: rgb(var(--color-foreground));
margin-bottom: 2rem;
opacity: 0.8;
}
.onhow-scroll-to-top {
display: inline-flex;
align-items: center;
gap: 0.5rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 0.875rem 1.75rem;
border-radius: 50px;
font-family: var(--font-body-family);
font-size: 0.95rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
text-transform: uppercase;
letter-spacing: 0.5px;
position: relative;
overflow: hidden;
opacity: 0;
transform: translateY(20px);
animation: onhow-button-appear 0.6s ease-out 0.3s forwards;
}
.onhow-scroll-to-top::before {
content: '↑';
font-size: 1.1em;
font-weight: bold;
}
.onhow-scroll-to-top::after {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
transition: left 0.5s ease;
}
.onhow-scroll-to-top:hover {
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
background: linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%);
}
.onhow-scroll-to-top:hover::after {
left: 100%;
}
.onhow-scroll-to-top:active {
transform: translateY(-1px);
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
.onhow-scroll-to-top.onhow-hidden {
opacity: 0;
transform: translateY(20px);
pointer-events: none;
}
#onhow-loading-1 {
left: 0;
animation-delay: 0.6s;
}
#onhow-loading-2 {
left: 29px;
animation-delay: 0.75s;
}
#onhow-loading-3 {
left: 58px;
animation-delay: 0.9s;
}
#onhow-loading-4 {
left: 88px;
animation-delay: 1.05s;
}
#onhow-loading-5 {
left: 117px;
animation-delay: 1.2s;
}
#onhow-loading-6 {
left: 146px;
animation-delay: 1.35s;
}
#onhow-loading-7 {
left: 175px;
animation-delay: 1.5s;
}
#onhow-loading-8 {
left: 205px;
animation-delay: 1.64s;
}
[class^="pagination"], [class^="Pagination"], .pagination {
display: none !important;
}
.js .features--show-element-staggering .ProductList--grid div.ProductItem {
visibility: visible;
}
.js .product-card[reveal-on-scroll="true"] {
opacity: 1;
}
@keyframes onhow-loading-wave {
0% {
transform: scale(1);
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
100% {
transform: scale(0.3);
background: linear-gradient(135deg, #e2e8f0 0%, #cbd5e0 100%);
}
}
@keyframes onhow-button-appear {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 750px) {
.onhow-scroll-to-top {
padding: 0.75rem 1.5rem;
font-size: 0.9rem;
}
.onhow-end-text {
font-size: 1.125rem;
}
.onhow-loading-container {
width: 200px;
transform: scale(0.9);
}
}
</style>
<script>
class InfiniteScroll extends HTMLElement {
constructor() {
super();
this.initGrid();
this.init();
this.page = 1;
this.scrollToTopBtn = null;
this.setupGlobalScrollListener();
}
initGrid() {
const gridOptions = [".ProductListWrapper", "#main-collection-product-grid", "#product-grid", ".product-list"];
for (let gridName of gridOptions) {
let grid = document.querySelector(gridName);
if (grid) {
this.gridName = gridName;
this.gridContainer = grid;
break;
}
}
}
init() {
const observer = new IntersectionObserver(this.revealItem.bind(this), {
root: null,
threshold: 0.05,
});
observer.observe(this);
}
setupGlobalScrollListener() {
let scrollTimeout;
window.addEventListener('scroll', () => {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
this.handleScrollVisibility();
}, 10);
});
}
handleScrollVisibility() {
if (this.scrollToTopBtn) {
const scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
const shouldShow = scrollPosition > 300;
if (shouldShow) {
this.scrollToTopBtn.classList.remove('onhow-hidden');
} else {
this.scrollToTopBtn.classList.add('onhow-hidden');
}
}
}
fetchNextPage(pageNum) {
if (pageNum <= this.dataset.pageSize) {
const url = `{{ shop.url }}/${this.dataset.next}`;
const urlObj = new URL(url);
urlObj.searchParams.set("page", pageNum);
const finalUrl = urlObj.href;
fetch(finalUrl, {
method: "GET",
headers: { "Content-Type": "text/html" }
})
.then(response => response.text())
.then(html => {
const parser = new DOMParser().parseFromString(html, "text/html");
const newContent = parser.querySelector(this.gridName).innerHTML;
this.gridContainer.insertAdjacentHTML("beforeend", newContent);
})
.catch(error => {
console.error("Error loading next page:", error);
});
}
if (pageNum == this.dataset.pageSize) {
this.showEndSection();
}
}
showEndSection() {
this.innerHTML = `
<div class="onhow-end-section">
<h2 class="onhow-end-text">${this.dataset.endText}</h2>
<div class="onhow-scroll-to-top onhow-hidden">
Scroll To Top
</div>
</div>
`;
this.setupScrollToTop();
}
setupScrollToTop() {
this.scrollToTopBtn = this.querySelector('.onhow-scroll-to-top');
if (this.scrollToTopBtn) {
this.scrollToTopBtn.addEventListener('click', this.scrollToTopAction.bind(this));
setTimeout(() => {
this.handleScrollVisibility();
}, 100);
}
}
revealItem(entries, observer) {
const [entry] = entries;
if (!entry.isIntersecting) return;
if (entry.target) {
this.page++;
this.fetchNextPage(this.page);
}
if (this.page == this.dataset.pageSize) {
observer.unobserve(entry.target);
}
}
scrollToTopAction() {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
}
}
customElements.define("infinite-scroll", InfiniteScroll);
</script>CODE : Replace the code below at line -185 in main-collection-product-grid.liquid
{%- if paginate.pages > 1 -%}
{% render 'pagination', paginate: paginate, anchor: '' %}
{%- endif -%}With The Code Below
{%- if paginate.pages > 1 -%}
{% render 'infinite-scroll',
next_page: paginate.next.url,
page_size: paginate.pages,
end_text: "You reached the end..."
%}
{%- endif -%}
