Skip to content
Published Authored byBilly Reiner

Schema · How-to

Shopify BreadcrumbList schema

BreadcrumbList is the Schema.org type for the navigation trail from the homepage to the current page1. Google requires at least two ListItem entries, each with position, name, and item (a URL)2. Most Shopify themes render visible breadcrumb HTML — "Home / Collection / Product Name" — but do NOT emit BreadcrumbList JSON-LD on every template. The 2026 install pattern: author per-template BreadcrumbList blocks inside main-product.liquid, main-list-collections.liquid, main-article.liquid, and the page template, using Liquid variables to build the trail dynamically.

Why BreadcrumbList earns its install slot on every Shopify storefront: it's a 60-second job per template, it replaces the URL path with the breadcrumb trail in the Google SERP, and AI engines parse it as a cheap entity-relationship signal. The schema is one of the highest-leverage low-effort installs in the full library.

What BreadcrumbList is

Per Schema.org v30.0, BreadcrumbList is 'a chain of linked Web pages, typically described using at least their URL and their name, and typically ending with the current page.' Parent type ItemList (Thing > Intangible > ItemList > BreadcrumbList). The single required property: itemListElement, an array of ListItem objects, each with position (Integer starting at 1), name (Text — the displayed crumb label), and item (URL — the page each crumb points to).

Mental model: BreadcrumbList declares the hierarchy of the current page within the site. On a Shopify PDP at /collections/coastal-decor/products/coastal-vase-no-3, the natural breadcrumb trail is Home → Coastal Decor → Coastal Vase No. 3. That trail becomes a four-item ListItem array (the current product is the last item; Google's spec accepts the current page in the trail).

What Shopify themes emit

View-source audit on Dawn-derivative Shopify themes (verified 2026-05-22): visible breadcrumb HTML is rendered by most themes via snippets/breadcrumbs.liquid or equivalent, BUT BreadcrumbList JSON-LD is NOT consistently emitted. Some Shopify Theme Store themes do ship BreadcrumbList JSON-LD (some Shopify-built premium themes); most third-party themes do not. The audit answer is template-specific, not platform-specific.

Action: view source on a PDP and find-on-page for BreadcrumbList. If absent, the install below is for you. If present, audit which templates ship it (PDP only? Collection pages? Blog posts?) and fill in the gaps.

BreadcrumbList fields

The fields per ListItem inside itemListElement: position (Integer, starting at 1, sequential), name (Text — the crumb label shown to the user), item (URL — the destination page). The last ListItem traditionally points to the current page; recent Google guidance accepts either including or omitting item on the last crumb. Both forms validate.

  • position — required. Sequential integer starting at 1. Skipping integers (1, 3, 4) triggers validator errors.
  • name — required. The user-facing breadcrumb label. On Shopify: collection.title, product.title, page.title, etc. Pass through | escape.
  • item — required (or omitted on the last crumb per recent Google guidance). Full URL. On Shopify: {{ shop.url }}{{ collection.url }}.

JSON-LD example per Shopify template

The block below is BreadcrumbList for a Shopify PDP. Paste it inside main-product.liquid alongside the Product block. The Liquid logic detects whether the PDP was reached via a collection-prefixed URL (/collections/foo/products/bar) and conditionally includes the collection crumb.

JSON-LD BreadcrumbList inside main-product.liquid — handles collection-prefixed URLs
 <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": [ { "@type": "ListItem", "position": 1, "name": "Home", "item": "{{ shop.url }}/" }{%- if collection -%}, { "@type": "ListItem", "position": 2, "name": "{{ collection.title | escape }}", "item": "{{ shop.url }}{{ collection.url }}" }{%- endif -%}, { "@type": "ListItem", "position": {%- if collection -%}3{%- else -%}2{%- endif -%}, "name": "{{ product.title | escape }}", "item": "{{ shop.url }}{{ product.url }}" } ] } </script> 

Equivalent blocks for main-list-collections.liquid (Home → collection), main-article.liquid (Home → blog → article), and the page template (Home → page) follow the same pattern with the appropriate Liquid variables.

Filtered collection URLs and BreadcrumbList

Shopify's default robots.txt blocks /collections/*+* URLs (filtered duplicates). When a user navigates a collection page with active filters (e.g. /collections/coastal-decor/+blue+ceramic), the URL is non-canonical and excluded from crawling. BreadcrumbList JSON-LD on that filtered page is wasted — the page won't be indexed anyway. Either gate BreadcrumbList emission on the unfiltered collection.url, or simply emit the block regardless and let robots.txt do its job.

Validation

Rich Results Test against a PDP with BreadcrumbList emitted should report Breadcrumbs detected, two or more ListItems parsed, zero errors. The Rich Results Test does NOT explicitly check that the position integers are sequential — the Schema.org Markup Validator does. Run both against representative templates.

A subtle gotcha: if your theme already emits a BreadcrumbList JSON-LD block AND you add your own, the page now has two BreadcrumbList objects. Google's validator handles this (no error) but the duplicate adds page weight for no benefit. Either remove the theme's block by editing snippets/breadcrumbs.liquid or skip authoring your own where the theme already covers it.

Shopify gotchas on BreadcrumbList

Four gotchas. First: missing | escape on collection.title or product.title (apostrophes and quotes break the JSON). Second: hard-coding item URLs without {{ shop.url }} prefix (the breadcrumb item becomes a relative path which Google's validator accepts but downstream parsers handle inconsistently). Third: skipping the collection crumb on PDPs reached via /products/foo (some Shopify storefronts use direct /products URLs without a collection prefix — the breadcrumb should still include the product's primary collection if one exists, fetched via product.collections.first). Fourth: emitting the breadcrumb on filtered collection pages with the filtered URL as the item (use the canonical collection.url instead).

A fifth gotcha for international Shopify stores: BreadcrumbList item URLs should use the current locale's URL, not the primary locale. Shopify exposes request.locale and the linked-URL filter | within: collection on product objects — verify the URL chain still resolves correctly in each shipped locale.