Fixes · Collection filters
Shopify size filter shows products that are out of stock in that size
The fix
On its own, the Size filter shows any product that has the size as a variant, in stock or not. Tick the Availability filter’s In stock as well and Shopify checks stock in that size, but shoppers rarely tick it.
So have the theme do it: apply In stock whenever a size is picked, or skip products with no in-stock variant in the chosen size and leave sizes with nothing in stock out of the list. Or use a filter app that indexes each variant.
01Why a size filter doesn’t check stock
Shopify’s storefront filtering can filter by variant options such as Size, and by availability. Filters combine with AND, and the values within one filter with OR (Shopify, Storefront filtering). A Size filter asks whether the product has a variant whose size is the one chosen. A variant in that size that’s sold out still counts.
“A size filter matches any product that HAS that size, regardless of whether that size is in stock.”
Availability is a filter of its own. Shopify describes it as filtering “based on variant availability”, and doesn’t say whether it’s checked against the same variant as the size. Replies on that 2026 thread say it isn’t: that a product still passes for a sold-out size because another size is in stock. That isn’t what we found. On a live store we look after (22 Oct 2026), a product sold out only in XS showed for Size XS, dropped out once In stock was ticked too, and came back with Out of stock. A dress sold out in S but not in L dropped out for S and In stock, and stayed for L and In stock. So Shopify does match the two to the same variant, but only when the shopper ticks both, and few do.
The filter list has the same blind spot. Without In stock it offers every size any product in the collection carries, so a shopper can pick a size that leads to nothing. With In stock ticked, the same test showed every size with nothing in stock counting zero, so a theme that hides empty values drops them. On an older thread (Jun 2023) the answers are Flow and metafields, an app, or theme code, and none is marked as the solution.
02Make picking a size mean in stock
For store owners
See it for yourself
On a collection, choose a size, then open a few of the products and check that size is in stock on each.
Add the Availability filter, and know its limit
In the Search & Discovery app, under Filters, add Availability. A shopper who ticks In stock with a size gets only products in stock in that size. One who doesn’t still sees the sold-out ones.
Hide empty sizes
In Search & Discovery > Settings, under Filters > Empty values, choose Hide (Shopify Help). With In stock ticked, a size with nothing in stock is then left out, if your theme follows the setting.
Choose the fix
A change to your theme (below) keeps Shopify’s own filters and costs nothing to run. The simplest applies In stock for the shopper whenever they pick a size. A filter app that indexes each variant is the other way.
For developers: filter the grid by stock, and drop dead-end sizes
The simplest route: when a shopper picks a size, tick In stock for them, so Shopify does the matching. Its pages and counts then agree with the grid, with no limit on collection size. Availability has to be one of the store’s filters for the input to exist. In a theme whose filters are checkboxes in one form:
const form = document.querySelector('#FacetFiltersForm'); // your theme's filter form
form.addEventListener('input', (event) => {
const input = event.target;
if (input.name !== 'filter.v.option.size' || !input.checked) return;
const inStock = form.querySelector('input[name="filter.v.availability"][value="1"]');
if (inStock) inStock.checked = true;
}, true); // capture: runs before the theme reads the formThe shopper sees In stock among the active filters and can untick it. If you’d rather not change the filters they chose, filter in the theme instead. That’s what we shipped on the store below. Two changes in the collection section. First, in the product loop, skip any product with no in-stock variant in the chosen sizes:
{%- liquid
assign size_filter = collection.filters | where: 'param_name', 'filter.v.option.size' | first
assign wanted = size_filter.active_values | map: 'value'
-%}
{%- for product in collection.products -%}
{%- if wanted.size > 0 -%}
{%- assign size_option = product.options_by_name['Size'] -%}
{%- assign in_stock = false -%}
{%- if size_option -%}
{%- assign index = size_option.position | minus: 1 -%}
{%- for variant in product.variants -%}
{%- if variant.available and wanted contains variant.options[index] -%}
{%- assign in_stock = true -%}
{%- break -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
{%- unless in_stock -%}
{%- continue -%}
{%- endunless -%}
{%- endif -%}
{%- render 'product-card', product: product -%}
{%- endfor -%}Second, in the filter list, leave out sizes that nothing in the collection has in stock:
{%- liquid
assign instock_sizes = ''
if collection.all_products_count <= 250
paginate collection.products by 250
for product in collection.products
assign size_option = product.options_by_name['Size']
if size_option
assign index = size_option.position | minus: 1
for variant in product.variants
if variant.available
assign key = '|' | append: variant.options[index] | append: '|'
unless instock_sizes contains key
assign instock_sizes = instock_sizes | append: key
endunless
endif
endfor
endif
endfor
endpaginate
endif
-%}
{%- for filter in collection.filters -%}
{%- if filter.param_name == 'filter.v.option.size' -%}
{%- for value in filter.values -%}
{%- assign key = '|' | append: value.value | append: '|' -%}
{%- if instock_sizes != blank and value.active == false -%}
{%- unless instock_sizes contains key -%}
{%- continue -%}
{%- endunless -%}
{%- endif -%}
<label>
<input type="checkbox" name="{{ value.param_name }}" value="{{ value.value }}" {% if value.active %}checked{% endif %}>
{{ value.label }}
</label>
{%- endfor -%}
{%- endif -%}
{%- endfor -%}- Match the size by its position.
product.options_by_name['Size'].positionsays which of the variant’s options is the size (Shopify, the product object), so a value in another option can’t match by accident. Use the option name your store uses, in both places. - Liquid gives you at most 250 products per page (Shopify, paginate). The in-stock size list needs the whole collection in one pass, so build it only up to 250 products; bigger collections keep Shopify’s list and rely on the grid.
- The second
paginatereads the same?page=as the grid. On page 2 of a collection under 250 products it holds nothing, so the list must fall back to Shopify’s when the set comes back empty (instock_sizes != blankabove). Without that check, page 2 shows no sizes at all. collection.productsis already narrowed by the filters the shopper has ticked, so the list reflects those products. Once a size is ticked, another size shows only if a product that matched stocks it.- Skipping products doesn’t change Shopify’s counts or pages: a page can show fewer products, and the number beside each size still counts sold-out ones. Show your “no products” message when every product on a page is skipped and there’s no next page, or the grid sits blank.
- Keep a size the shopper has ticked in the list, even if it has sold out, so they can untick it (
value.activeabove).
03Try a sold-out size, then your biggest collection
Pick a size that’s sold out on some products
Those products shouldn’t appear; the ones in stock in that size should.
Open the size list
A size with nothing in stock in the collection shouldn’t be offered.
Link straight to a sold-out size
Add
?filter.v.option.size=and the size to the collection’s address. You should get the “no products” message, not a blank grid.Try your biggest collection
Over 250 products, the list stays Shopify’s and the grid still filters.
From our work
04A sportswear retailer’s outlet collection
On a sportswear retailer’s store we look after, the store’s team reported that the Size filter on its outlet collection showed products that weren’t available in the chosen size.
After (15 Sep 2026). The grid now skips any product with no in-stock variant in the chosen sizes. The Size list drops sizes with nothing in stock on collections of up to 250 products. The outlet holds more than that, so there the list stays Shopify’s and the grid does the work. When skipping leaves nothing on a page, the store’s “no products” message shows instead of a blank grid.
- In our log“Size filter now shows only products in stock in the chosen size”
The bigger picture
05A filter is a promise
A filter is a promise about what a shopper can buy. If your collections are large, change every day or carry many sizes, check what each filter actually returns, and what search returns too.
We’d make this change in the theme and keep Shopify’s own filters, rather than add an app to do it.
06Sources
Every step and claim above comes from one of these, or from the fix on the store it describes.
- Shopify developers: Storefront filtering
- Shopify Help Center: Search & Discovery filters (empty values)
- Shopify developers: Liquid objects, product
- Shopify developers: Liquid tags, paginate
- Shopify Community: Impulse theme, size filter shows out-of-stock sizes and unavailable variants (Jul 2026)
- Shopify Community: Filter showing out of stock sizes, Dawn theme (Jun 2023)
Sources checked on .
