Shopify Horizon Breadcrumbs: Free Liquid Section with SEO Schema

Shopify Horizon Breadcrumbs

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.

Breadcrumb Section

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 BreadcrumbList JSON-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

Leave a comment

Please note, comments need to be approved before they are published.