Metafield-Driven Breadcrumbs Across Product, Collection, Page, Blog and Article
One breadcrumb snippet whose ancestor trail is authored per product and per collection through a custom.breadcrumbs collection-list metafield, with a three-level fallback for products and natural trails for pages, blogs and articles — rendered as a real navigation landmark from a single argument-free {% render %}.
Shopify has no page hierarchy. A product URL is flat, product.collections is unordered and includes automated collections, and the collection object only exists when a shopper arrived from a collection URL. This snippet reads an ancestor trail merchandisers author in a custom.breadcrumbs collection-list metafield, falls back through the current collection's own trail, and renders one accessible <nav> across five template types with no arguments.
The Problem
Shopify has no page hierarchy. A product lives at /products/<handle> whether it belongs to one collection or twelve, and the platform does not record which of those collections is its "parent." Liquid offers two hints, and both lie. product.collections is unordered and includes automated collections, so a shampoo that is also in "New," "Bestsellers" and a price-band collection has four equally plausible parents. The collection object exists only when the shopper arrived through a collection URL; arrive from search or email and there is no context at all.
For a catalog organized by curl type, with a kids sub-brand and a bundle range, a product page with no sense of where it sits is a dead end, and a trail that guesses wrong sends a kids shopper to an adult collection. Merchandisers needed to decide the trail per product and per collection, not accept whatever Liquid resolved.
The Constraint
Nothing derivable was reliable. product.collections could not yield a canonical parent, and collection could not be counted on to exist. The only trustworthy source of an intended hierarchy was one a merchant writes down: a metafield holding an ordered list of ancestor collections.
The second constraint was shape. The trail had to appear on product, collection, page, blog and article templates, rendered from three different sections — the product section, the blog section and the collection banner — and it had to be one snippet with no arguments, because each section calls it from a different context and none of them should have to know how a trail is built. Everything the snippet needs, it has to find for itself in the objects Liquid exposes on the current template.
And the sub-brand mattered. Kids pages carry their own visual identity, and the trail had to be able to follow it without a second snippet.
What We Built
de_breadcrumb.liquid branches on template.name and always begins with Home, linked to routes.root_url.
Collections. For a collection template, the snippet reads collection.metafields.custom.breadcrumbs — a list of collection references — and renders every entry in order before the current collection's own title. The merchandiser writes the ancestry once, on the collection, and every path through it agrees.
Products, with a three-level fallback. For a product template it prefers product.metafields.custom.breadcrumbs, the product's own authored trail. If that is empty, it falls back to the current collection's custom.breadcrumbs followed by that collection — so a product reached through a collection inherits the collection's ancestry plus the collection itself. If there is no collection context either, it renders Home and the product title: a valid two-level trail rather than a broken one.
Everything else. Pages get Home and the page title. Blogs get Home and the blog. Articles nest under their blog, so an article's trail reads Home, the blog, the article. Any template the snippet does not recognize falls back to page_title, so a new template type never renders an empty landmark.
Markup. The trail is an <ol> inside <nav aria-label="Breadcrumb">. The current page carries aria-current="page", and the separators are inline SVGs marked aria-hidden, so a screen reader announces the items and not the chevrons. Every ancestor is a real anchor to a real collection URL, in the server-rendered HTML, so the hierarchy the merchandiser authored is the hierarchy a crawler follows. On mobile the list switches to a single horizontally scrolling row. The snippet is suppressed entirely on the index template, where a trail of one item is noise.
Why This Way
Driving the trail from a metafield makes hierarchy an editorial decision made once in the admin rather than a guess made at render time. A product that belongs to many collections has exactly one authored trail, the same trail however the shopper arrived. Rendering it as an <ol> with aria-current inside a labeled <nav> keeps it a real navigation landmark rather than decorative text.
What that costs is authoring. Every product and collection that should have a trail has to be given one, in a metafield, by a person; the fallback chain softens that but does not remove it. A product nobody has touched shows Home and its own name — honest, but flat. We accepted that because inferring parents from product.collections produces trails that are wrong in ways a merchandiser cannot fix, and a wrong trail is worse than a short one. A second cost lives in the fallback itself: the collection-derived trail depends on the collection object, so the same product can show a two-level trail from one entry point and a four-level one from another until its own metafield is filled in.
Why Not an App
Breadcrumb apps derive a trail from your navigation menus or from the collection the shopper came through, and most render it client-side after the page has painted. On this catalog both derivations fail for the reason the platform does: neither the menu nor the referrer is the hierarchy the brand intends.
This is under 200 lines of Liquid and CSS with no runtime cost. The trail is in the server-rendered HTML, its links are real links to real collections, and the ancestry lives in Shopify's own data model beside the product it describes rather than in a vendor's configuration screen.
Implementation Notes
- Rendered by
de_main_product.liquid,main-blog.liquidandmain-collection-banner.liquid— three sections, one snippet, no arguments. - Suppressed entirely on the index template via
{%- unless template == 'index' -%}. - The product branch reads
product.metafields.custom.breadcrumbsfirst, thencollection.metafields.custom.breadcrumbs, then the collection itself — three levels of fallback before the two-level default. custom.breadcrumbsis defined on both the product and the collection resource, so the same key means the same thing on both and a merchandiser learns one field.- Separator SVGs are repeated inline per item rather than sprited, which keeps the snippet dependency-free at the cost of some repeated markup.
- The trail's mobile treatment is a
nowraplist with horizontal overflow, so a deep trail scrolls rather than stacking into three lines under the page title.
Edge Cases
- A product reached without a collection context still renders a valid two-level trail — Home and the product — rather than an empty landmark or a crumb pointing nowhere.
- A product with no authored trail that is reached through a collection inherits that collection's ancestry plus the collection itself, so the trail is still meaningful before anyone has filled in the product's metafield.
- On mobile the list gets
overflow-x: autowithnowrap, so long trails scroll instead of wrapping. - The collection-hero variant of the trail is hidden on mobile, so a collection page does not show the same trail twice — once above the hero and once inside it.
- An article's trail nests under its blog, so a post is never presented as a top-level page.
- Any template the snippet does not branch on falls back to
page_title, so a future template type renders a two-level trail rather than nothing.
Platform Primitives Used
metafields—custom.breadcrumbs, a collection-reference list defined on both the product and the collection resource, is the entire data model.liquid-snippets— one argument-free snippet that reads its context from the template's own objects, called from three sections.
Where It Runs
Five template types — product, collection, page, blog and article — through three sections. On the product page it renders from the main product section; on collection pages from the collection banner; on the blog from the blog section, which also serves articles. The snippet itself is global in the sense that one file decides how every trail on the storefront is built.
What This Demonstrates
- Editorial blog and content hub — the primary capability: a blog and its articles placed in a real hierarchy, on the same trail the catalog uses.
- Breadcrumbs and site taxonomy — ancestry authored per record through metafields, across every template type the storefront serves.
- Technical SEO, indexation and page metadata — every crumb is a server-rendered link to a parent collection, so the intended hierarchy is crawlable from every product and article.
How We Know
One Liquid snippet from the theme, under 200 lines including its styles, plus two client metafield-export records. custom.breadcrumbs appears in those exports twice — once on the product resource and once on the collection resource, both marked as new fields. The exports establish that the field was defined and where; no working session in this corpus discusses breadcrumbs, so the request behind it is not recorded. The fallback chain and the template branching are read from the snippet.
Related Builds
- Adults/Kids audience-switching header with automatic menu swap — the same storefront's other answer to a two-audience catalog, at the top of the page rather than in the trail.
- Metafield-driven two-level FAQ accordion grafted into a help-center app's shell — the same storefront, the same pattern: structure authored in metafields, rendered by the theme.
- Metafield-driven editorial article template (TSB blog) — a different storefront's article page assembled from metafields, where the trail is one of the things the platform did not provide.
- Mega-menu header with separate mobile menu and image blocks — navigation on another storefront, where the hierarchy lives in menus rather than in metafields.
The Buy-vs-Build Question
A breadcrumb app buys a trail derived from something the platform already knows, injected after paint. Building it bought an authored hierarchy in the server-rendered HTML and cost a metafield someone has to fill in. The larger question — how far Shopify's blog and content model can be pushed before an external system is the better answer — is the subject of the editorial hub and blog at scale decision page.
Provenance & Evidence
- Client: LUS Brands — loveurcurls.com
- Surfaces: Product page, collection page, blog; the snippet is global
- Templates served: five types — product, collection, page, blog, article
- Complexity: Medium
- Attribution: Deploi-authored. The snippet and the metafield convention are ours. It runs in a theme built on Dawn 15.4.1 and is called from three sections.
- Status: Live, verified 2026-09-06
- Evidence: One Liquid snippet from the theme, plus two client metafield-export records
- Confidence: Strong — the exports confirm the field; the code confirms the mechanism
- Primary capability: Editorial blog and content hub
Ready for a Store That Knows Where Every Page Sits?
You dream it. We build it. If your product pages are dead ends and your breadcrumb app keeps guessing wrong, Contact us today and we'll put the hierarchy where your merchandisers can own it.