A collection page can look correct in Shopify Admin and still ship a broken default view.
In this production case, the availability filter said the collection had:
- In stock: 3 products
- Out of stock: 5 products
But the unfiltered collection initially rendered only the three in-stock product cards. When a shopper opened the filter drawer and manually selected Out of stock, the missing five products appeared.
That is the part worth taking seriously. The filter count knew the products existed. The collection data was not empty. The problem was the storefront's default rendering path.
The symptom
The default collection page behaved as if an availability filter had already been applied, even when the shopper had not selected one.
That created three separate risks:
- Shoppers saw an incomplete catalog by default.
- Crawlers and AI agents reviewing the collection saw fewer product links than the collection actually contained.
- Merchandising logic became confusing because the filter UI contradicted the product grid.
For some stores, hiding sold-out products is intentional. That is fine when the collection rules, copy, and filters are designed around it. This was different: the UI exposed an Out of stock option with a count, but the default grid dropped those products until the user touched the filter.
The misleading first assumption
The easy assumption is that Shopify is not returning unavailable products.
That is not where I would start.
Before changing a Storefront API query, check three surfaces:
- The collection query result.
- The initial HTML or serialized loader data.
- The rendered product card list.
In this case, the missing products were present in the data path. They were just not making it into the initial rendered grid.
That pointed away from Shopify Admin settings and toward the Hydrogen route loader.
Where the bug actually lived
The collection loader was doing a reasonable thing at first glance: prioritize in-stock products for the first page, then keep unavailable products in a separate buffer so they could be shown later.
The trap was pagination state.
The app treated Shopify's connection pageInfo.hasNextPage as the only signal that more products could appear. But the app had already moved some products into a local out-of-stock buffer.
So when Shopify said there was no next remote page, the storefront stopped. It ignored the unavailable products it already had in memory.
That is a classic custom storefront bug: a platform pagination flag gets used as if it also describes app-local state.
The safer rendering rule
The fix was not to remove the in-stock priority. The fix was to make the priority complete.
The rule became:
- Show in-stock products first.
- If there are not enough in-stock products to fill the initial collection grid, top up the grid from the unavailable buffer.
- Keep load-more available while either Shopify has another page or the app has buffered unavailable products left.
The shape of the fix was simple enough to describe without hiding it behind framework terms:
- Start the initial grid with the available products.
- Count how many slots are still empty.
- If there are empty slots, append products from the unavailable buffer.
- Keep the load-more state true when either Shopify has another remote page or the local unavailable buffer still has products.
- Treat
hasNextPage as one pagination signal, not as the whole storefront state.
The important detail is not the exact variable names. It is the separation between remote pagination and local buffering.
Shopify's hasNextPage tells you whether there are more results to fetch from that connection. It does not know that your loader already partitioned products into available and unavailable arrays.
Before and after proof
The useful verification was not just a passing build.
The collection needed to prove the shopper-facing behavior changed:
- Before the fix, the unfiltered page rendered 3 visible product links.
- The availability filter still showed 3 in-stock and 5 out-of-stock products.
- Selecting the out-of-stock filter made the hidden products appear.
- After the fix, the unfiltered page rendered all 8 expected product links.
- Out-of-stock labels remained visible where needed.
- The build and targeted storefront checks passed before the change went live.
That last point matters. A collection-grid bug can look small, but it affects product discovery, internal linking, and the public crawl surface of the store.
The debugging checklist I would reuse
For a Hydrogen collection page where sold-out products disappear unexpectedly, I would check this in order:
- Confirm whether the store actually wants out-of-stock products visible by default.
- Compare filter counts against visible product cards.
- Inspect the initial HTML or loader payload before blaming Shopify.
- Trace every use of availableForSale and availability filters.
- Check whether unavailable products are being buffered, sliced, or deferred.
- Treat pageInfo.hasNextPage as remote pagination only, not as a complete app-state flag.
- Test unfiltered, In stock, Out of stock, and load-more behavior separately.
- Re-check the production page after deploy, because collection pages are often cached.
The bug is usually not one line. It is the interaction between a merchandising preference and custom pagination code.
The SEO and AI visibility angle
Out-of-stock products are not always worthless pages.
For catalog-heavy or luxury stores, an unavailable item can still support discovery, brand authority, internal linking, and future purchase intent. The store might choose to hide it, deindex it, keep it visible, or show it only under a filter.
But that should be a deliberate merchandising and SEO decision.
It should not happen because the Hydrogen loader filled a local buffer and then forgot to render it.
The practical lesson
When a Hydrogen storefront has custom collection logic, test the default route as carefully as the filtered route.
Filters can accidentally hide the real problem because selecting one causes a fresh query or a different rendering branch. The unfiltered collection page is the page most shoppers, crawlers, and AI agents see first.
If that default state is wrong, the fix belongs in the data-loading and rendering contract, not in a copy change or a merchant workaround.
Your Shopify store works, but edge cases keep surfacing after launch? That is when I come in. If your Hydrogen collections, filters, or product state are drifting from what shoppers and crawlers should see, I can help trace the real data path and fix the storefront without turning it into a broad rebuild.