This page is aimed at a merchant’s theme developer: the person editing the
collection section, pagination snippet and facets of the Shopify theme.
Overview
Shopify collections have a single global product order that applies to all markets. To merchandise each market differently, Depict stores market-specific orderings indepict_multi_market_collection metaobjects,
keyed by collection ID and market ID. This integration makes your collection
section read products from those metaobjects instead of the collection’s
default order.
A collection version
test rides on the same
integration: Depict switches the test on for stores that have steps 1 to 5
in place. Collection version tests lists what
a test needs from the theme.
The same integration also supports hiding out-of-stock (OOS) products from
collection pages while keeping their product pages reachable. This is
controlled by two metafields set from the Depict portal: one on the shop
(hide globally) and one per collection.
Implementation
Update your existingmain-collection.liquid (or equivalent collection
section) as follows. The snippets use Dawn’s structure; adapt the markup to
your theme.
1. Add market detection logic
Place this at the top of the section, outside the{% paginate %} block.
It resolves the per-market metaobject, reads the two hide-OOS metafields,
and skips Depict ordering whenever filters or a custom sort are active.
collection_{id}_market_fallback, which covers
the main/default market and any market that has not been merchandised.
2. Compute the real page count
When OOS products are hidden, Shopify’s nativepaginate object (based on
collection.products) over-counts, because hidden products still count
toward the total. The depict_multi_market_collection wrapper carries an
in_stock_counts JSON field that maps every enabled market ID to that
market’s in-stock product count, for example
{"93309862172":3000,"93309894940":3000}.
Each count is the number of products in the collection that are
storefront-visible (status ACTIVE) and in stock for that market. It is
always less than or equal to collection.products_count. A larger count is
a Depict-side bug: report it rather than working around it in the theme.
In-stock counts are currently enabled per merchant; rollout to all
multi-market merchants is pending a canary week. The old
depict_collection_data metaobject is dead. Do not reference it in new
integrations.depict_in_stock_count. Resolve multi_market_collection and the count
at the top of the section (no paginate context needed), but compute
metaobject_pages inside the {% paginate %} block: paginate.page_size
is nil outside it and dividing by nil silently yields a wrong page count.
localization.market.idrenders as a plain number; the| append: ''string normalisation is required for keying the JSON hash.section.settings.products_per_pageis Dawn’s page size. In general, divide by whatever feeds your outerpaginate.- If the field or the market key is missing, everything degrades to native
pagination (
nilbecomesblankand the override goes unused).
3. Add custom product rendering logic
Products render from the chunked metaobject arrays (products_0,
products_1, …), manually tracking which ones fall inside the current page
window. Replace the product rendering loop with the following. The scan cap
lines are explained right after the snippet.
- With OOS hiding on, only available products count toward the page offset and index.
- It breaks out of the loop as soon as the current page window is filled
(
product_index >= end_paginate_offset), even when OOS hiding is on, so it never walks the full product set. if products.count == 0 breakis safe because Liquid drops references it cannot resolve, so an all-invisible chunk reads as empty and ends the walk early. Depict guarantees (since August 2026) that synced orders contain only storefront-visible products. If a collection stops rendering midway while deeper pages exist, suspect unresolvable references and report it to Depict.
The scan cap is mandatory
depict_scan_cap caps the scan at the in-stock count so requests never walk
the OOS tail. It does two things:
- Skips the walk entirely on pages past the last real page (the
{% unless … current_offset >= depict_scan_cap %}wrapper). - Stops at the count-th in-stock match (the extra
breakat both existing break points).
<link rel="next">.
4. Fix pagination for OOS filtering
Feedmetaobject_pages from step 2 through your theme’s pagination plumbing.
When the count is unavailable, metaobject_pages stays blank and everything
falls through to the native values.
Infinite scroll
If your theme uses infinite scroll, find where it sets the total page count on a data attribute (for exampledata-last-page) and override it:
Pagination buttons
If your theme renders pagination buttons, passmetaobject_pages as a
total_pages_override parameter to the pagination snippet:
paginate.parts and
paginate.pages reflect the unfiltered collection size, so three things need
adjusting.
-
Compute the effective total at the top of the snippet, and use
effective_pagesinstead ofpaginate.pageswherever the total is referenced (visibility checks, mobile-hide logic, and so on): -
Skip page links beyond the real total. Inside the loop over
paginate.parts, add an early skip:Ellipsis (…) parts past the effective last page survive this pruning because they are not links. This is cosmetic only. -
Fix the “next” arrow. The native
paginate.nextmay point to a page beyond the real total, so add a check on the current page:
5. Fix the total products count
Themes show a product count in the filters bar or facets area (Dawn:snippets/facets.liquid). With OOS hiding active,
collection.products_count still includes the hidden products. Use the
in-stock count when it applies:
total_products_to_display wherever the total is shown: the filters
bar, the load-more “X of Y” text, and any results header.
Collection version tests
A collection version test pits two saved versions of one collection against each other. Depict’s app embed serves the test and reorders the grid in the shopper’s browser; the section needs no extra Liquid. Depict switches the test on per store once steps 1 to 5 are in place, so market versions and tests render the same way.How a test reaches the storefront
When a test starts, Depict writes each variant’s order as adepict_market_products metaobject and, for every market in scope, a
depict_multi_market_collection metaobject with the handle
collection_{collection id}_market_{market id}_abtest_{test id}_variant_A
(and _variant_B) whose product_order points at it. These sit next to the
market’s own collection_{collection id}_market_{market id} object and
never replace it.
On every collection page view the Depict Brand Features app embed reads
the collection’s running test, resolves both variants for
localization.market.id, and hands them to the Depict script. The script
assigns the shopper to a group once per browser, fetches that variant’s
whole order from the Storefront API, and reorders the product cards your
section rendered for the page. New tests also serve each variant’s
content cards, product duplicates and image settings. A
running test outranks the market’s own order for the shopper’s group. When
the test ends, the live version’s order applies again; promoting a winner
publishes it like any other version, so it reaches the storefront through
steps 1 to 5.
Because the section renders the live order’s products for each page, which
page a product is on follows the live order; its position within the page
follows the variant.
What the theme must provide
- Depict’s grid markup. The
<depict-grid-start-beacon>and<depict-grid-end-beacon>elements around the product loop, anddata-depict-product-id="{{ product.id }}"on every product card or an element inside it. See Content features theme integration. Depict’s theme patcher adds these; keep them when you rework the section, or the script cannot find the grid or match the cards to the variant’s order. - Steps 1 to 5 above, on the theme that serves the test. Depict switches collection version tests on per store once they are in place.
- The Depict Brand Features app embed switched on for the theme. See Theme setup. The storefront access key the script uses is written by the app; nothing to add.
The group is stored per browser in
localStorage under
depict_collection_ab_variant_{test id}, so a returning shopper keeps
their group on the same device. Filtered and custom-sorted views keep the
theme’s native order for both groups, exactly as the market order does in
step 1; the group is still assigned. Inside the Depict portal’s live
preview no test is served.Verify on the storefront
- Start a test in the Depict app on the collection.
-
Open the collection in two different browsers, in a market the test
covers. Private windows of one browser share storage, so they land in the
same group. In each browser’s console, read the group:
Repeat in a fresh browser profile until one reads
Aand the otherB. -
In each browser, check that the
#depict-grid-rendering-scriptelement has a non-emptydata-collection-ab-testattribute and a non-emptydata-storefront-access-keyattribute. An empty test attribute means the embed found no running test for this market, or one of the variant metaobjects has no products. Without the access key the script serves no test and never writes the group key. - Compare the order of the page’s products between the browsers with the two versions in the Depict app. The order follows each browser’s group.

