Shopify Liquid Pagination: The SEO Pattern You're Missing
Most Shopify developers use JavaScript for collection pagination, unknowingly damaging SEO. I'll show you why Liquid's built-in `{% paginate %}` with `rel=prev/next` is the only way to protect your crawl budget and get your products indexed.
Ashraful
Shopify Select Partner

When I audit Shopify stores, I've seen this 50+ times on client audits: a beautiful collection page that loads more products as you scroll, or a slick 'Load More' button. It looks modern, it feels fast to the user. But it's a disaster for SEO, and it’s killing your product visibility. The core issue? Most developers completely miss the proper Shopify Liquid pagination pattern, opting instead for JavaScript-based solutions that leave countless products in the dark, unindexed by search engines. If you're using anything other than native {% paginate %} with rel=prev/next for your collection, search, or blog pages, you're actively preventing Google from seeing your store's full inventory.
Why Shopify Liquid Pagination Matters for SEO
Search engines, at their core, are designed to follow links. When they crawl your Shopify store, they discover pages by traversing the links embedded in your HTML. Your collection pages, especially those with many products, are critical pathways for discovery. If these pathways are broken or obscured by JavaScript, Googlebot can't find and index your valuable product pages.
Shopify provides a robust, built-in solution for pagination using its Liquid templating language: the {% paginate %} tag. This tag automatically generates server-side pagination links, complete with unique URLs for each page (e.g., /collections/your-collection?page=2). More importantly, it integrates seamlessly with the rel="prev" and rel="next" attributes, which are crucial signals for search engines to understand the sequential relationship between paginated pages. This tells Google: "Hey, this isn't duplicate content; it's part of a larger series." Without this, you're asking Google to guess, and search engines don't guess – they move on.
The Mistake I See Most Often: Client-Side Pagination
What most agencies get wrong is prioritizing perceived user experience over fundamental search engine crawlability. They implement custom JavaScript to fetch more products, often using fetch or XMLHttpRequest with an offset parameter to load products dynamically. This might make the page feel faster to a human user because they don't experience a full page reload, but it's a silent killer for your SEO. The problem is simple: Googlebot (and other search engine crawlers) executes JavaScript, but it doesn't always do so perfectly, and it certainly doesn't always wait for all dynamic content to load before moving on. Even if it did, the dynamically loaded content often lacks a unique, crawlable URL.
When you use JavaScript to load more products without changing the URL, you're essentially showing Googlebot only the first page of products. All subsequent products, loaded via AJAX, exist in a crawlable black hole. I've seen stores with thousands of products where only the first 50-100 were indexed because the rest were hidden behind a 'Load More' button that Google couldn't effectively interact with or attribute to a distinct URL. This isn't just about missing out on new products; it's about wasting your crawl budget – the number of pages Google is willing to crawl on your site in a given period. Every uncrawlable product is a missed opportunity for organic traffic.
Here's a simplified example of what I often see, which looks fine to the user but is an SEO dead-end:
// collection-load-more.js
document.getElementById('load-more-button').addEventListener('click', function() {
const currentPage = parseInt(this.dataset.currentPage);
const nextPage = currentPage + 1;
fetch(`/collections/my-collection.json?limit=24&page=${nextPage}`)
.then(response => response.json())
.then(data => {
// Append new products to the DOM
// Update currentPage dataset
})
.catch(error => console.error('Error loading products:', error));
});
This JavaScript approach fetches product data, but it doesn't create new, unique URLs that Google can follow. The URL in the browser's address bar remains /collections/my-collection, regardless of how many times the user clicks 'Load More'. For Google, it's just one page.
The Correct Shopify Liquid Pagination Pattern (with rel=prev/next)
The solution is built right into Shopify: the {% paginate %} tag. It's designed to handle pagination natively and correctly. This ensures that each page of your collection has its own unique URL, which is the cornerstone of good SEO for large catalogs.
Let's look at the standard implementation within collection.liquid (or search.liquid, blog.liquid, etc.):
{% comment %}
Set the number of products per page. This is usually 12, 24, or 48.
Keep this consistent across your store.
{% endcomment %}
{% assign products_per_page = 24 %}
{% paginate collection.products by products_per_page %}
<div class="product-grid">
{% for product in collection.products %}
{% render 'product-card', product: product %}
{% endfor %}
</div>
{% if paginate.pages > 1 %}
<nav class="pagination">
<ul>
{% if paginate.previous %}
<li>{{ link_to_previous_page: 'Previous' }}</li>
{% endif %}
{% for part in paginate.parts %}
{% if part.is_link %}
<li>{{ link_to_page: part.title }}</li>
{% else %}
<li class="current">{{ part.title }}</li>
{% endif %}
{% endfor %}
{% if paginate.next %}
<li>{{ link_to_next_page: 'Next' }}</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% endpaginate %}
This Liquid code snippet does several things correctly:
- Unique URLs: Each paginated page gets a distinct URL (e.g.,
/collections/my-collection?page=2). - Server-Side Rendering: The links are generated on the server, meaning they are present in the HTML that Googlebot first receives, without needing JavaScript execution.
rel=prevandrel=next: Shopify automatically injects these critical attributes into the<head>of your document when{% paginate %}is used. This is often overlooked, but it's the signal that tells search engines: "These pages are part of a continuous sequence." This helps Google consolidate ranking signals across the series, preventing perceived duplicate content issues and ensuring all pages are properly understood.
To confirm, check your theme.liquid (or a file included there that handles <head> content). You should see something like this, which Shopify adds automatically when {% paginate %} is active on a page:
{% comment %}
This is a simplified example. Shopify automatically adds rel=prev/next
when {% paginate %} is used on the current page.
You don't need to manually add these if you're using paginate correctly.
This is just to illustrate what Shopify does for you.
{% endcomment %}
{% if paginate.previous %}
<link rel="prev" href="{{ paginate.previous.url }}" />
{% endif %}
{% if paginate.next %}
<link rel="next" href="{{ paginate.next.url }}" />
{% endif %}
This automatic inclusion is why you must use {% paginate %}. It handles the nuances of sequential page signaling that custom JS implementations almost always miss.
Protecting Your Crawl Budget: A Real-World Example
I recently worked with a large fashion retailer, an established brand moving hundreds of SKUs daily, but their new arrivals and seasonal collections were consistently struggling to rank. Sales were plateauing despite high brand awareness. During my initial audit, I immediately spotted the problem: their collection pages used a custom 'infinite scroll' implemented with JavaScript. While visually appealing, only the first 30 products on any collection page were truly indexed by Google. Thousands of products deeper in their catalog were practically invisible to organic search.
The fix was straightforward, though it required a complete overhaul of their collection template: we ripped out the custom JavaScript and reimplemented all collection, search, and vendor pages using the native {% paginate %} Liquid tag. We styled the pagination links to be clean and modern, ensuring a smooth user experience even with full page reloads.
Within weeks, we saw a dramatic increase in indexed pages via Google Search Console. New product launches started gaining organic visibility much faster. Within three months, their organic traffic from collection pages climbed by 28%, directly correlating to improved product indexing. This wasn't a magic trick; it was simply aligning with how search engines are designed to crawl the web. If your current theme development isn't yielding these kinds of results, it's time to re-evaluate. You can find out more about my approach to robust theme development at ezomfy.com/services/theme-dev.
Implementing {% paginate %}: Practical Steps and Considerations
Using {% paginate %} isn't just about dropping a tag into your code; it requires thoughtful placement and consideration to maximize its SEO benefits.
Where to Place Your Pagination
The {% paginate %} tag is most effective when applied to templates that display a list of items:
collection.liquid: For product collections.search.liquid: For search results pages.blog.liquid: For blog post listings.customers/orders.liquid: Even for customer order history, if you want that indexed (though less common for SEO).
Always wrap the entire loop of items you want to paginate within the {% paginate %} block. This ensures that the pagination object (paginate) has access to all the necessary information to generate correct links.
Customizing Pagination Links
Shopify's link_to_page, link_to_previous_page, and link_to_next_page filters provide basic HTML for your pagination. You'll want to apply your own CSS to make them fit your theme's aesthetic. Remember, the goal is functional, crawlable links, not just pretty ones. Ensure they are clearly visible and clickable. Avoid styling that makes them look like static text or hides them.
Avoiding Common Pitfalls
- Don't Hide
rel=prev/next: Some developers try to hide the default Shopify-generated pagination links withdisplay: nonein CSS while simultaneously trying to implement their own JavaScript pagination. This completely defeats the purpose. If you use{% paginate %}, let it generate the links and therel=prev/nexttags, and style those links. - Canonical URLs: Always ensure your canonical tags are correct. Shopify generally handles this well, but custom themes or app integrations can sometimes introduce issues. The canonical URL for a paginated page should point to itself (e.g.,
/collections/my-collection?page=2should canonicalize to/collections/my-collection?page=2), not the first page of the collection, unless explicitly intended for a very specific SEO strategy (which is rare for pagination). - Performance: While
{% paginate %}requires full page reloads, modern Shopify themes are optimized. Focus on overall page speed – image optimization, efficient Liquid code, minimal render-blocking resources – rather than trying to 'fix' pagination with JavaScript at the expense of SEO.
I built ezomfy.com to help Shopify merchants navigate these kinds of technical SEO challenges. If your store's organic visibility isn't where it should be, or if you suspect your current pagination setup is holding you back, I offer a free 30-minute consultation. We can quickly pinpoint issues and map out a practical path forward. Visit ezomfy.com/consultation to book your call.
About the author
Ashraful
Shopify Select Partner, Top Rated Plus on Upwork. 700+ Shopify projects shipped over 7+ years — themes, apps, migrations, speed, Hydrogen. Solo shop, no agency middlemen.
Read the full storyWorking on a Shopify project?
That's what I do every day. Pick whichever feels lower-friction.
More from the blog
Keep reading

Why Your Shopify Theme Needs Theme Check in CI
Shopify Theme Check prevents Liquid errors before they hit production, yet most agencies skip it in CI. I'll show how to integrate it into GitHub Actions to block faulty PRs, saving countless emergencies.
Read
Shopify Section vs. Block: Master When and How to Use Them (with Code)
Shopify developers and merchants often misuse sections for everything. I'll show you why blocks are crucial for repeating content and how getting this wrong leads to unmaintainable themes in months.
Read
The One Liquid Snippet That Drops LCP by 800ms on Most Shopify Stores
Discover a single Liquid snippet that can slash your Shopify store's Largest Contentful Paint (LCP) by hundreds of milliseconds, dramatically improving speed and user experience.
Read