Pushy
By ·

Why Liquid Can't Push Sold-Out Products Down on Shopify (Pagination Explained)

Search for how to sort Shopify products by inventory and you’ll find the same eight lines of Liquid in every forum thread and Stack Overflow answer. Split the collection into available and unavailable products, loop over the first group, then loop over the second.

The idea is right. On a small collection it does exactly what you want. On a real catalog it fails quietly, and you won’t catch it unless you go looking at page 1 after you’ve already declared victory.

This is what the code does, why it breaks, and what has to change instead.

The snippet everyone copies

{% assign in_stock = collection.products | where: "available", true %}
{% assign out_of_stock = collection.products | where: "available", false %}

{% for product in in_stock %}
  {% render 'product-card', product: product %}
{% endfor %}

{% for product in out_of_stock %}
  {% render 'product-card', product: product %}
{% endfor %}

Drop that into your collection template and available products render before unavailable ones. It costs nothing, it depends on no third party, and you can read the whole thing in one sitting.

Why it looks like it works

Test this on a collection of 30 products and it’s flawless. Sold-out items sink to the bottom, in-stock items rise, and you move on.

That result is real, and it’s also the trap. A collection under 50 products fits entirely in one page, so collection.products hands your template every product in the collection. The filter has the full set to work with, so it sorts the full set.

The moment a collection outgrows a single page, your template stops receiving the full set. Nothing errors. Nothing looks broken in the theme editor. The sort just stops meaning what you think it means.

The pagination wall

collection.products is not “every product in this collection.” It’s a page of them.

Shopify decides which products land on the current page using the collection’s stored sort order, the one you picked in the admin under Best selling, Alphabetical, Manual, and so on. It does that server-side, before your template runs. By the time where: "available" executes, the products for this page have already been chosen and handed to you.

So your filter reorders the page. It cannot reach the products that Shopify assigned to page 2.

Here’s what that looks like on an actual store. Take a 300-product collection showing 24 per page, sorted by Best selling. Eight of your top 24 sellers are out of stock, which is normal, because products sell out precisely because they sell well. Those eight are in the top 24 by lifetime sales, so Shopify puts them on page 1. Your snippet then moves them to positions 17 through 24.

They are still on page 1. A shopper who scrolls the first page sees all eight of them, just lower down than before.

Shopify’s own docs note the limit that sits underneath this: referencing collection.products fetches up to 50 products by default, regardless of what your for loop asks for. This exact behavior comes up over and over in the Shopify developer forums, usually from someone who has already shipped the snippet and is trying to work out why page 1 still looks wrong.

Your code sorts the page. Shopify built the page. That’s the entire bug.

The five things people try next

Raise the products per page. {% paginate collection.products by 50 %} gets you a bigger page, and 50 is the documented ceiling for products. A 300-product collection still needs six pages, you’ve just moved the boundary. You’ve also asked the server to render 50 product cards on every collection view, which is a real cost on mobile.

Drop the paginate tag entirely. Doesn’t help. Outside a paginate block, collection.products still fetches up to 50 by default. The paginate tag also can’t reach past the 25,000th item in an array, so there is a hard ceiling here regardless of approach.

Reorder with JavaScript after the page loads. This one is popular and it’s the worst of the options. The HTML that Googlebot receives still has sold-out products first, so search engines index the order you were trying to fix. Shoppers get a visible reshuffle after first paint. And any theme with AJAX filtering or infinite scroll re-renders the grid from the server, which throws your reordering away. It also has the same blind spot as the Liquid version, because the JavaScript can only see the products already on the page.

Sort by a metafield. Shopify’s collection sort options are a fixed list, and none of them read a metafield. You can write inventory data to a metafield all you like, there’s nothing to sort by it with.

Automate it with Shopify Flow. Flow reacts to inventory changes and can add or remove a tag, which makes it useful for automated collection conditions. It has no action that repositions a product inside a collection, so it can tag the problem but not fix it.

What actually fixes it

The stored sort order has to change. Not the render order in your theme, the order Shopify uses to build the pages in the first place.

Once the collection itself is ordered in-stock first, pagination inherits that for free. Page 1 contains available products because they are genuinely first in the collection now, not because a template shuffled them after the fact. Everything downstream reads from that same order, so a crawler and a shopper end up looking at the same page.

That’s an Admin API operation, writing product positions to the collection. Themes can’t do it. Liquid renders, it doesn’t write back.

Method comparison

Liquid two-loopJavaScript reorderAPI-level sort
ScopeCurrent page onlyCurrent page onlyWhole collection
Survives paginationNoNoYes
What changesDisplay orderDisplay orderThe real collection order
Crawlers see the sortNoNoYes
Survives theme updatesNoNoYes
Works with AJAX filtersPartlyNoYes

Doing it without writing the API code

Pushy does the API-level version of this. It rewrites the actual collection order so out-of-stock products sit at the bottom, and returns each one to its original position when it’s restocked.

  1. Install Pushy from the Shopify App Store.
  2. Open it in your Shopify admin, where all your collections are listed.
  3. Select the collections you want managed, one or all of them.
  4. Turn auto-push on. Pushy re-sorts the selected collections immediately.
  5. It then watches inventory and re-sorts as stock changes, with an hourly sync as a backstop.

Because it operates at the API level, there’s no theme code involved, which means a theme update can’t undo it and crawlers see the same order your shoppers do. You can exclude products by tag if you want something pinned regardless of stock. It’s free for up to 5 collections, and paid plans run $3.99 to $14.99 a month.

If your collections genuinely fit on one page, the Liquid snippet is fine and you should use it. This becomes worth paying for at the point where your catalog paginates.

Frequently Asked Questions

Why do sold-out products still show on page 1 when my Liquid sorts them last?

Because Shopify chose the products for page 1 before your template ran, using the collection's stored sort order. Your where: "available" filter only reorders the products already assigned to the current page, so sold-out items move to the bottom of page 1 rather than off it.

Can I fix this by increasing the products per page?

Not really. The paginate tag caps at 50 products per page, so any collection with more than 50 products still paginates and still has the same problem. Larger pages also mean more product cards rendered on every collection view, which slows the page down on mobile.

Does collection.products return every product in the collection?

No. Shopify's Liquid documentation states that referencing collection.products fetches up to 50 products by default, regardless of the limit set on your for loop. Templates receive a page of products, not the full collection.

Does reordering products with JavaScript hurt SEO?

It doesn't help. The HTML delivered to crawlers keeps the original order, so search engines index sold-out products in the positions you were trying to change. Shoppers also see the grid visibly rearrange after the page paints, and themes with AJAX filtering or infinite scroll discard the reordering when they re-render.

Can I sort a Shopify collection by inventory using a metafield?

No. Shopify's collection sort options are a fixed set (Best selling, Alphabetical, Price, Date, Manual) and none of them can sort by a metafield. Writing stock data to a metafield gives you nothing to sort with.

What is the only reliable way to sort in-stock products first?

Change the collection's stored order through the Admin API so in-stock products come first, which makes pagination inherit the correct order. Pushy does this automatically and restores each product's original position when it comes back in stock.

Get started

If you’ve already shipped the Liquid snippet and page 1 still looks wrong, this is why. Install Pushy for free and let the collection order fix itself.