Shopify Horizon does not include breadcrumb navigation by default. In this guide, we will add dynamic Shopify Horizon breadcrumbs using one copy-and-paste Liquid section.
The section follows your Shopify navigation menu, supports products, collections, pages, blogs, and articles, and includes ready-to-use BreadcrumbList schema for SEO.
You can see the same breadcrumb section working at the top of this article.
Why Use a Navigation Menu Instead of Metafields?
Some Shopify breadcrumb tutorials use collection metafields to define parent collections.
That method works, but it creates a second hierarchy that must be maintained separately from your navigation menu. When the menu changes, the breadcrumb metafields may become outdated.
This section uses your Shopify menu as the source of truth. Reorganize the menu, and the breadcrumb path updates automatically.
SEO Benefits of This Breadcrumb Section
The section includes BreadcrumbList JSON-LD structured data out of the box.
- The visible breadcrumb and schema use the same path.
- Every item includes its name, URL, and position.
- Current pages use Shopify’s canonical URL.
- Search engines can better understand your site hierarchy.
- No separate schema app or manual JSON-LD setup is required.
Breadcrumb schema does not guarantee a special Google search result, but it gives search engines clear and consistent information about the relationship between your pages.
1. Create the Breadcrumb Section
From Shopify Admin, go to Online Store → Themes → Edit code.
Inside the Sections folder, create a new file named:
breadcrumbs.liquid
Paste the complete code below into the new section and save it.
{%- liquid
assign supported_pages = 'product,collection,page,blog,article' | split: ','
assign page_type = request.page_type
assign separator = '/'
assign breadcrumb_path = ''
assign breadcrumb_depth = 0
assign breadcrumb_menu = section.settings.breadcrumb_menu
assign breadcrumb_links = breadcrumb_menu.links
if section.settings.section_width == 'full-width'
assign section_width_class = 'section--full-width section--full-width-margin'
else
assign section_width_class = 'section--page-width'
endif
-%}
{%- if supported_pages contains page_type -%}
{%- comment -%}
Product breadcrumb priority:
1. Use the collection context from the current product URL.
2. If no context exists, use the deepest matching collection
assigned to the product.
{%- endcomment -%}
{%- if page_type == 'product' -%}
{%- if collection != blank -%}
{%- assign target_handle = collection.handle -%}
{%- for level_1 in breadcrumb_links -%}
{%- if level_1.type == 'collection_link'
and level_1.object.handle == target_handle
-%}
{%- assign breadcrumb_path = level_1.object.handle -%}
{%- assign breadcrumb_depth = 1 -%}
{%- break -%}
{%- endif -%}
{%- for level_2 in level_1.links -%}
{%- if level_2.type == 'collection_link'
and level_2.object.handle == target_handle
-%}
{%- capture candidate_path -%}
{%- if level_1.type == 'collection_link' -%}
{{- level_1.object.handle -}}|
{%- endif -%}
{{- level_2.object.handle -}}
{%- endcapture -%}
{%- assign breadcrumb_path = candidate_path | strip -%}
{%- assign breadcrumb_depth = 2 -%}
{%- break -%}
{%- endif -%}
{%- for level_3 in level_2.links -%}
{%- if level_3.type == 'collection_link'
and level_3.object.handle == target_handle
-%}
{%- capture candidate_path -%}
{%- if level_1.type == 'collection_link' -%}
{{- level_1.object.handle -}}|
{%- endif -%}
{%- if level_2.type == 'collection_link' -%}
{{- level_2.object.handle -}}|
{%- endif -%}
{{- level_3.object.handle -}}
{%- endcapture -%}
{%- assign breadcrumb_path = candidate_path | strip -%}
{%- assign breadcrumb_depth = 3 -%}
{%- break -%}
{%- endif -%}
{%- endfor -%}
{%- if breadcrumb_path != blank -%}
{%- break -%}
{%- endif -%}
{%- endfor -%}
{%- if breadcrumb_path != blank -%}
{%- break -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
{%- if breadcrumb_path == blank and product.collections.size > 0 -%}
{%- for product_collection in product.collections -%}
{%- assign target_handle = product_collection.handle -%}
{%- for level_1 in breadcrumb_links -%}
{%- if level_1.type == 'collection_link'
and level_1.object.handle == target_handle
-%}
{%- if breadcrumb_depth < 1 -%}
{%- assign breadcrumb_path = level_1.object.handle -%}
{%- assign breadcrumb_depth = 1 -%}
{%- endif -%}
{%- endif -%}
{%- for level_2 in level_1.links -%}
{%- if level_2.type == 'collection_link'
and level_2.object.handle == target_handle
-%}
{%- capture candidate_path -%}
{%- if level_1.type == 'collection_link' -%}
{{- level_1.object.handle -}}|
{%- endif -%}
{{- level_2.object.handle -}}
{%- endcapture -%}
{%- assign candidate_path = candidate_path | strip -%}
{%- assign candidate_handles = candidate_path | split: '|' -%}
{%- assign candidate_depth = candidate_handles.size -%}
{%- if candidate_depth > breadcrumb_depth -%}
{%- assign breadcrumb_path = candidate_path -%}
{%- assign breadcrumb_depth = candidate_depth -%}
{%- endif -%}
{%- endif -%}
{%- for level_3 in level_2.links -%}
{%- if level_3.type == 'collection_link'
and level_3.object.handle == target_handle
-%}
{%- capture candidate_path -%}
{%- if level_1.type == 'collection_link' -%}
{{- level_1.object.handle -}}|
{%- endif -%}
{%- if level_2.type == 'collection_link' -%}
{{- level_2.object.handle -}}|
{%- endif -%}
{{- level_3.object.handle -}}
{%- endcapture -%}
{%- assign candidate_path = candidate_path | strip -%}
{%- assign candidate_handles = candidate_path | split: '|' -%}
{%- assign candidate_depth = candidate_handles.size -%}
{%- if candidate_depth > breadcrumb_depth -%}
{%- assign breadcrumb_path = candidate_path -%}
{%- assign breadcrumb_depth = candidate_depth -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- endfor -%}
{%- endfor -%}
{%- endfor -%}
{%- endif -%}
{%- elsif page_type == 'collection' -%}
{%- assign target_handle = collection.handle -%}
{%- for level_1 in breadcrumb_links -%}
{%- if level_1.type == 'collection_link'
and level_1.object.handle == target_handle
-%}
{%- if breadcrumb_depth < 1 -%}
{%- assign breadcrumb_path = level_1.object.handle -%}
{%- assign breadcrumb_depth = 1 -%}
{%- endif -%}
{%- endif -%}
{%- for level_2 in level_1.links -%}
{%- if level_2.type == 'collection_link'
and level_2.object.handle == target_handle
-%}
{%- capture candidate_path -%}
{%- if level_1.type == 'collection_link' -%}
{{- level_1.object.handle -}}|
{%- endif -%}
{{- level_2.object.handle -}}
{%- endcapture -%}
{%- assign candidate_path = candidate_path | strip -%}
{%- assign candidate_handles = candidate_path | split: '|' -%}
{%- assign candidate_depth = candidate_handles.size -%}
{%- if candidate_depth > breadcrumb_depth -%}
{%- assign breadcrumb_path = candidate_path -%}
{%- assign breadcrumb_depth = candidate_depth -%}
{%- endif -%}
{%- endif -%}
{%- for level_3 in level_2.links -%}
{%- if level_3.type == 'collection_link'
and level_3.object.handle == target_handle
-%}
{%- capture candidate_path -%}
{%- if level_1.type == 'collection_link' -%}
{{- level_1.object.handle -}}|
{%- endif -%}
{%- if level_2.type == 'collection_link' -%}
{{- level_2.object.handle -}}|
{%- endif -%}
{{- level_3.object.handle -}}
{%- endcapture -%}
{%- assign candidate_path = candidate_path | strip -%}
{%- assign candidate_handles = candidate_path | split: '|' -%}
{%- assign candidate_depth = candidate_handles.size -%}
{%- if candidate_depth > breadcrumb_depth -%}
{%- assign breadcrumb_path = candidate_path -%}
{%- assign breadcrumb_depth = candidate_depth -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- endfor -%}
{%- endfor -%}
{%- endif -%}
{%- if breadcrumb_path != blank -%}
{%- assign breadcrumb_handles = breadcrumb_path | split: '|' -%}
{%- endif -%}
<div
class="section-background color-{{ section.settings.color_scheme }}"
></div>
<div
class="
section
{{ section_width_class }}
spacing-style
breadcrumb-wrapper
color-{{ section.settings.color_scheme }}
"
style="{% render 'spacing-padding', settings: section.settings %}"
>
<nav class="breadcrumb" aria-label="Breadcrumb">
<ol class="breadcrumb__list" role="list">
<li class="breadcrumb__item">
<a
class="breadcrumb__link link focus-inset"
href="{{ routes.root_url }}"
>
Home
</a>
<span
class="breadcrumb__separator"
aria-hidden="true"
>
{{ separator }}
</span>
</li>
{%- case page_type -%}
{%- when 'product' -%}
{%- if breadcrumb_handles != blank -%}
{%- for handle in breadcrumb_handles -%}
{%- assign crumb_collection = collections[handle] -%}
{%- if crumb_collection != blank -%}
<li class="breadcrumb__item">
<a
class="breadcrumb__link link focus-inset"
href="{{ crumb_collection.url }}"
>
{{ crumb_collection.title }}
</a>
<span
class="breadcrumb__separator"
aria-hidden="true"
>
{{ separator }}
</span>
</li>
{%- endif -%}
{%- endfor -%}
{%- endif -%}
<li
class="breadcrumb__item breadcrumb__item--current"
>
<span aria-current="page">
{{ product.title }}
</span>
</li>
{%- when 'collection' -%}
{%- if breadcrumb_handles != blank -%}
{%- for handle in breadcrumb_handles -%}
{%- assign crumb_collection = collections[handle] -%}
{%- if crumb_collection != blank -%}
{%- if crumb_collection.handle == collection.handle -%}
<li
class="breadcrumb__item breadcrumb__item--current"
>
<span aria-current="page">
{{ crumb_collection.title }}
</span>
</li>
{%- else -%}
<li class="breadcrumb__item">
<a
class="breadcrumb__link link focus-inset"
href="{{ crumb_collection.url }}"
>
{{ crumb_collection.title }}
</a>
<span
class="breadcrumb__separator"
aria-hidden="true"
>
{{ separator }}
</span>
</li>
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- else -%}
<li
class="breadcrumb__item breadcrumb__item--current"
>
<span aria-current="page">
{{ collection.title }}
</span>
</li>
{%- endif -%}
{%- when 'page' -%}
<li
class="breadcrumb__item breadcrumb__item--current"
>
<span aria-current="page">
{{ page.title }}
</span>
</li>
{%- when 'blog' -%}
<li
class="breadcrumb__item breadcrumb__item--current"
>
<span aria-current="page">
{{ blog.title }}
</span>
</li>
{%- when 'article' -%}
<li class="breadcrumb__item">
<a
class="breadcrumb__link link focus-inset"
href="{{ blog.url }}"
>
{{ blog.title }}
</a>
<span
class="breadcrumb__separator"
aria-hidden="true"
>
{{ separator }}
</span>
</li>
<li
class="breadcrumb__item breadcrumb__item--current"
>
<span aria-current="page">
{{ article.title }}
</span>
</li>
{%- endcase -%}
</ol>
</nav>
</div>
{%- capture breadcrumb_schema_items -%}
{%- assign schema_position = 1 -%}
{%- assign home_url = shop.url | append: routes.root_url -%}
{
"@type": "ListItem",
"position": {{ schema_position }},
"name": "Home",
"item": {{ home_url | json }}
}
{%- assign schema_position = schema_position | plus: 1 -%}
{%- case page_type -%}
{%- when 'product' -%}
{%- if breadcrumb_handles != blank -%}
{%- for handle in breadcrumb_handles -%}
{%- assign crumb_collection = collections[handle] -%}
{%- if crumb_collection != blank -%}
{%- assign crumb_url = shop.url
| append: crumb_collection.url
-%}
,{
"@type": "ListItem",
"position": {{ schema_position }},
"name": {{ crumb_collection.title | json }},
"item": {{ crumb_url | json }}
}
{%- assign schema_position = schema_position | plus: 1 -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
,{
"@type": "ListItem",
"position": {{ schema_position }},
"name": {{ product.title | json }},
"item": {{ canonical_url | json }}
}
{%- when 'collection' -%}
{%- if breadcrumb_handles != blank -%}
{%- for handle in breadcrumb_handles -%}
{%- assign crumb_collection = collections[handle] -%}
{%- if crumb_collection != blank -%}
{%- assign crumb_url = shop.url
| append: crumb_collection.url
-%}
,{
"@type": "ListItem",
"position": {{ schema_position }},
"name": {{ crumb_collection.title | json }},
"item": {{ crumb_url | json }}
}
{%- assign schema_position = schema_position | plus: 1 -%}
{%- endif -%}
{%- endfor -%}
{%- else -%}
,{
"@type": "ListItem",
"position": {{ schema_position }},
"name": {{ collection.title | json }},
"item": {{ canonical_url | json }}
}
{%- endif -%}
{%- when 'page' -%}
,{
"@type": "ListItem",
"position": {{ schema_position }},
"name": {{ page.title | json }},
"item": {{ canonical_url | json }}
}
{%- when 'blog' -%}
,{
"@type": "ListItem",
"position": {{ schema_position }},
"name": {{ blog.title | json }},
"item": {{ canonical_url | json }}
}
{%- when 'article' -%}
{%- assign blog_url = shop.url | append: blog.url -%}
,{
"@type": "ListItem",
"position": {{ schema_position }},
"name": {{ blog.title | json }},
"item": {{ blog_url | json }}
}
{%- assign schema_position = schema_position | plus: 1 -%}
,{
"@type": "ListItem",
"position": {{ schema_position }},
"name": {{ article.title | json }},
"item": {{ canonical_url | json }}
}
{%- endcase -%}
{%- endcapture -%}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{{ breadcrumb_schema_items | strip }}
]
}
</script>
{%- endif -%}
{% stylesheet %}
.breadcrumb-section {
position: relative;
}
.breadcrumb {
display: flex;
width: 100%;
overflow-x: auto;
scroll-behavior: smooth;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
}
.breadcrumb::-webkit-scrollbar {
display: none;
}
.breadcrumb__list {
display: flex;
align-items: center;
min-width: max-content;
margin: 0;
padding: 0;
list-style: none;
color: rgb(var(--color-foreground));
opacity: 0.65;
}
.breadcrumb__item {
display: flex;
align-items: center;
white-space: nowrap;
}
.breadcrumb__link.link,
.breadcrumb__separator,
.breadcrumb__item--current {
font-size: 12px;
}
.breadcrumb__link.link {
color: inherit;
text-decoration: none;
transition: opacity
var(--animation-speed)
var(--animation-easing);
}
.breadcrumb__link.link:hover {
opacity: 0.75;
text-decoration: none;
}
.breadcrumb__separator {
margin-inline: var(--margin-xs, 0.4rem);
color: inherit;
}
.breadcrumb__item--current {
color: rgb(var(--color-foreground));
opacity: 1;
}
{% endstylesheet %}
{% schema %}
{
"name": "Breadcrumbs",
"class": "breadcrumb-section",
"settings": [
{
"type": "header",
"content": "t:content.appearance"
},
{
"type": "select",
"id": "section_width",
"label": "t:settings.section_width",
"options": [
{
"value": "page-width",
"label": "t:options.page"
},
{
"value": "full-width",
"label": "t:options.full"
}
],
"default": "full-width"
},
{
"type": "color_scheme",
"id": "color_scheme",
"label": "t:settings.color_scheme",
"default": "scheme-1"
},
{
"type": "link_list",
"id": "breadcrumb_menu",
"label": "Breadcrumb menu",
"default": "main-menu"
},
{
"type": "header",
"content": "t:content.padding"
},
{
"type": "range",
"id": "padding-block-start",
"label": "t:settings.top",
"min": 0,
"max": 100,
"step": 1,
"unit": "px",
"default": 8
},
{
"type": "range",
"id": "padding-block-end",
"label": "t:settings.bottom",
"min": 0,
"max": 100,
"step": 1,
"unit": "px",
"default": 8
}
],
"presets": [
{
"name": "Breadcrumbs"
}
]
}
{% endschema %}
2. Select Your Breadcrumb Menu
Go to Shopify Admin → Content → Menus.
You can create a dedicated menu for breadcrumbs, but in most cases, we recommend using your store's existing Main menu. This keeps your breadcrumb hierarchy consistent with the navigation customers already use, so you only need to maintain one menu structure.
Clothing
Shirts
T-Shirts
Each menu item that should appear in the breadcrumb should link directly to a Shopify collection.
Whenever you update your Main menu, the breadcrumb hierarchy updates automatically as well, keeping both navigation systems perfectly in sync.
3. Add the Section to the Header Group
Open the Shopify Theme Editor and select the Header Group.
Add the new Breadcrumbs section directly below the main header, then select the navigation menu you created.

The Header Group is the recommended location because you only need to add the section once. It will then appear automatically across all supported templates:
- Product pages
- Collection pages
- Standard pages
- Blog pages
- Article pages
You can still add the section to an individual template when a page requires a different layout.
How Product Breadcrumb Paths Are Selected
When a product is opened from a collection page, that collection path is used first.
/collections/t-shirts/products/classic-cotton-t-shirt
When the product is opened directly, the section checks the collections assigned to the product and selects the deepest matching path from the chosen menu.
This gives more specific collections priority over broader categories.
Example
The breadcrumb at the top of this article is generated by the same section.
Home / Shopify theme blocks / Shopify Horizon Breadcrumbs: Free Liquid Section with SEO Schema
A product breadcrumb may look like this:
Home / Clothing / Shirts / T-Shirts / Classic Cotton T-Shirt
What Is Included?
- One copy-and-paste Liquid section
- No additional snippet required
- No JavaScript
- No Shopify app
- No collection metafields
- Menu-based collection hierarchy
- Product collection-context support
- Automatic
BreadcrumbListJSON-LD - Horizontal scrolling on smaller screens
- Horizon color scheme and spacing settings
Important Notes
- Search pages are not included in this version.
- Products without a matching menu collection display
Home / Product title. - Collections not found in the selected menu display
Home / Collection title. - The styling uses Horizon theme utilities and may require small adjustments in other Shopify themes.
- If a collection appears in several menu locations, the deepest matching path is used.
This Shopify Horizon breadcrumb section was developed by Hyrik Studio. Feel free to reuse or customize it for your own Shopify projects.
0 comments