Error: pagination always returns the same 50 elements

Error: Shopify pagination returns the first 50 items on all pages

Nadine Thery
UmamiTech Blog
Published in
6 min readMar 28, 2023

--

It turns out that the paginate object interacts strangely with the collection object and it’s not clearly documented. Check how we solved this problem and our findings.

Last week all of a sudden (and on a weekend 🔥🔥) our pagination in collections pages started behaving weirdly by returning the same 50 items on all the pages. No errors, no warnings. It was not even falling into the “empty state”. Our users were not able to browse our collection items!

Even though the number of pages was calculated correctly and the links were correct, the returned items never changed. What’s more, if we asked for 49 items or less, it worked perfectly 🤯.

tl;dr

If you need to check if a collection has products do not use collection.products.size to check if a collection has items. Use collection.products_count instead.

It turns out that paginate has a complicated relationship with the collection.products array, and affects/modifies it even when referenced out of the paginate object, causing unexpected behavior of the pagination.

The key point here is that the Shopify documentation doesn’t explain anything about this.

Some background: about iteration limits and the paginate object

As stated in Shopify docs of the iteration (for loops), you can only perform 50 iterations per page. So if you want to render a collection with more than 50 products (or in general), you have to use the paginate object in order to make batches of 50 items and be able to iterate over them (the pages).

Our debugging process 🐛

  1. The basics: any recent changes?

The devasting reply to this was: NO. There were no recent deploys or changes in the Admin that could justify a sudden change in the pagination behavior.

2. Is this happening in older versions of our theme? Is it happening using the Dawn theme?

We tried older versions of our code and it wasn't happening, it didn’t happen using Dawn either. So we could isolate that there was indeed a problem in our current theme, and it wasn’t something happening globally.

3. Try to lower the number of items per page

Given Shopify's limitations to iterations, we thought that the problem could do with the number of items on the page. So we asked for 49 products, and it worked!

Despite this looking like the solution, this only left us with more questions.

  1. Why did it fail all of a sudden? It was working yesterday!
  2. Why can’t we ask for 50 items if it is allowed in the docs?
  3. Why is it working with 49 items?!

This finding allowed us to apply a quick fix and restore the functionality for our users temporarily, but it was far from solving the issue. So we kept investigating.

When we saw the pagination working again with this change

Let’s go line by line… and the solution.

When we initially implemented the pagination, we followed Dawn’s code. But with some custom lines. For example, we don’t want to show the product grid if the collection is empty. Dawn, instead, shows the grid after the pagination is initiated, with an “empty message” in it.

This was our original snippet to show a grid of 50 products with pagination:

 <div class="collection__product-grid-container" id="collection__product-grid-container" data-id="{{ section.id }}">
{% if collection.products.size > 0 %}
{% paginate collection.products by 50 %}
<div id="product-grid" class="grid">
{%- liquid
for product in collection.products
assign can_purchase = false

if product.available
if service_status == 'full_service'
assign can_purchase = true
else
assign service_collection = product.collections | where: 'handle', service_status_handle | first

if service_collection
assign can_purchase = true
endif
endif
endif
if can_purchase
render 'product-card', product: product, lazy: true
endif
endfor
-%}
</div>
{%- if paginate.pages > 1 -%}
{% render 'comp-paginator', paginate_object: paginate %}
{%- endif %}
{% endpaginate %}

{% else %}
<div id="no-results" class="fh fv fc mc ws tc p15 pb60">
{% render 'svg-cup-empty' %}
<h2 class="t-h1 pt15">Looks like we don't have anything here.</h2>
</div>
{% endif %}
</div>

We use {% if collection.products.size %} to check the number of products inside the products array. If we find out that the products array size is 0, it means that this collection doesn’t have any products, right?

Our testing collection has around 250 items (which means we have 5 pages of pagination — of 50 items each). So we said…

— “hang on… what’s the result of collection.products.size ? it should be 250, right?”

Well, it wasn’t. It was 50 .

When we saw 50, but we were expecting 250

Let’s take a look at the docs again… maybe we missed something? 🤔

So… “all the products in the collection”… 😑 but it’s not returning that. Have we reached a dead-end?

It can’t be the end…

WAIT, we scrolled down a bit and stumbled across this property:

What if we try this one? The result was 250. Great, that’s what we expected to see. 🎉 So, we replaced the collection.product.size with collection.products_count.

And yes, that solved the issue and pagination worked as expected again. At this point, we have a consistent solution, and a giveaway:

When using pagination, do not use collection.products.sizeto check if a collection has items. Use collection.products_count instead.

Ok, but you didn’t tell me why

Our guess is that the paginate object mutates the collection.products array, even when it is called out of the array. Still, we cannot explain why the pagination shows the same results over and over again (we’ll let Shopify do that… maybe).

The real issue here is that the paginate object suffers some kind of side effect when you try to access the collection.products array before the paginateis declared (and this is not documented anywhere).

At this point, we had our solution, yet we really wanted to understand why this was happening. So we raised the question to the Shopify Frontend team. For us, collection.products.size should be a valid way to check the size of the array according to the docs.

Please find below the support answer:

It looks like this is currently expected behaviour on any theme in the Online Store — From my findings, it seems that any reference to {{ collection.products.size }} will depend on pagination results; and therefore if this object is referenced before the Liquid logic paginates items, it results in problematic behaviour. There is a relatively simple fix for this however — and that is to put any reference to the {{ collection.products.size }} inside of the {% pagination %} object. With this rule in mind, you can still achieve the same intended outcome with a small code change.

So, Shopify’s team suggests using collection.products.size only inside the paginate to avoid “problematic behaviour”. This definitely fixes the issue as well, but it still doesn’t explain why the pagination is crazy (and why it was out of nowhere).

In our code, we don’t want to check if the returned page has products, we wanted to check if the collection had products if the first place. Because if it doesn’t, we don’t need the pagination nor the grid to be rendered in the first place. However, it is another possible solution to this same issue.

So, the other giveaway:

When using paginate, any reference to collection.productseven if it is out the paginateobject, will be affected (transformed) by the pagination, and the paginate will crash silently.

We just wanted to share this, since the docs are not clear and it costed us some research time :)

Peace & code

Nadine

--

--

Nadine Thery
UmamiTech Blog

Frontend Developer 👩🏻‍💻, videogamer 🎮, boardgamer 🎲, plant-lady 🌿, mother-of-cat-and-dogs 🐱🐶🐶, environment-concerned🌎, youtuber, ocassional podcaster