{{craftSnippets}}
Home Articles Plugins Starter theme Components on Github Gists About
  • Home
  • Articles
  • Breadcrumb created from URL for Craft CMS
Posted on Apr 05, 2019 by Piotr Pogorzelski
Updated on Jul 01, 2019

Breadcrumb created from URL for Craft CMS

Breadcrumb template component on github gists
Breadcrumb based on Seomatic on github gists
Breadcrumb component automatically created from URL. Each URL segment is represented by a single breadcrumb link.
Table of contents:
  • Component overview
  • How does it work?
  • Custom route links
  • Breadcrumb with Seomatic
  • Why is such breacrumb component useful?
  • Breadcrumb plugin
  • Article update history

Component overview #

This template component can be dropped into any project - it works out of the box, without any modifications. Here are it's features summed up:

  • Component is styled using bulma classes.
  • It has google structured data attributes to improve your SEO.
  • It also has proper ARIA attributes. ARIA stands for Accessible Rich Internet Applications - it's standard which seeks to help people with disabilities navigate websites using tools like screen readers.
  • Breadcrumb will only show up if multiple links exist, since breadcrumb with just one link is not really useful.

Breadcrumb component itself:

{# v4 #}
{# http://craftsnippets.com/articles/breadcrumb-created-from-url-for-craft-cms #}

{# settings #}
{% set nonElementLinks = false %}

{# populate breadcrumbLinks array if no array of links was provided #}
{% if breadcrumbLinks is not defined %}
    {% set breadcrumbLinks = [] %}
    {# home #}
    {% set home = craft.app.getElements().getElementByUri('__home__', currentSite.id) %}
    {% set breadcrumbLinks = breadcrumbLinks|merge([{
        url: home.url ?? alias(currentSite.baseUrl),
        title: home.title ?? 'homepage'|t,
    }]) %}
    
    {# get elements #}
    {% set segments = craft.app.request.segments %}
    {% for segment in segments %}
            {% set uriPart = segments[0:loop.index]|join('/')|literal %}
            {% set element = craft.app.elements.getElementByUri(uriPart, currentSite.id) %}
            {% if element %}
                {% set breadcrumbLinks = breadcrumbLinks|merge([{
                    url: element.url,
                    title: element.title,
                }]) %}
            {% elseif nonElementLinks %}
                {% set breadcrumbLinks = breadcrumbLinks|merge([{
                    url: url(uriPart),
                    title: segment|t,
                }]) %}
            {% endif %}
    {% endfor %}
{% endif %}

{# render breadcrumb #}
{% if breadcrumbLinks|length > 1 %}
<nav class="breadcrumb" aria-label="{{'breadcrumbs'|t}}">
    <ul itemscope itemtype="http://schema.org/BreadcrumbList">
        {% for link in breadcrumbLinks %}
            <li class="{{loop.last ? 'is-active'}}" {{loop.last ? 'aria-current="page"' }} itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
                <a href="{{link.url}}" itemtype="http://schema.org/Thing" itemprop="item">
                    <span itemprop="name">{{ link.title }}</span>
                </a>
                <meta itemprop="position" content="{{loop.index}}" />
            </li>
        {% endfor %}
    </ul>
</nav>
{% endif %}

How does it work? #

For each URL segment, element query using getElementByUri method is performed. This query uses URI parameter that is composed of this segment and all segments preceding it. If the query is successful, title of the returned object is used as specific breadcrumb link text value. getElementByUri will go through all elements that have URI attribute - entries, categories and even custom ones added by plugins.

First breadcrumb link is not represented by URL segment - it is homepage link. It is associated with entry with empty URI, queried by special slug __home__. title of that entry will be used as text value of link. If no such entry exists, string homepage passed through translation filter will be used instead.

Custom route links #

If getElementByUri method returns nothing, it means that URL segment is not associated with entry, category or any other custom element page. Most likely it represents some custom URL route. In such a situation, you can do one of these things:

  • Set up an array of links yourself and pass it manually to the component as breadcrumbLinks - like this: {% include 'breadcrumb' with {breadcrumbLinks: breadcrumbLinks} %}. If component receives such array, it will not generate links itself but use these passed to it.
  • Set variable nonElementLinks at begining of the component to true. This will cause links not associated with elements pages to be appended to the breadcrumb. Their text values will be taken from URL segment and passed trough |t filter so you can set them up in static translations.

Breadcrumb with Seomatic #

If you are already using Seomatic on your website, you can use it's built-in functionality to get data required to render links. Here's breadcrumb component adjusted for Seomatic:

{# v4 #}
{% set breadcrumbLinks = seomatic.jsonLd.get('breadcrumbList').itemListElement %}
{% if breadcrumbLinks|length > 1 %}
<nav class="breadcrumb" aria-label="{{'breadcrumbs'|t}}">
    <ul>
        {% for link in breadcrumbLinks %}
            <li class="{{loop.last ? 'is-active'}}" {{loop.last ? 'aria-current="page"'}}>
                <a href="{{ link.item['@id'] }}">
                    <span itemprop="name">{{ link.item['name'] }}</span>
                </a>
                <meta itemprop="position" content="{{loop.index}}" />
            </li>
        {% endfor %}
    </ul>
</nav>
{% endif %}

Note that this version of breadcrumb is stripped of it's structured data attributes - Seomatic renders proper JSON-LD code for breadcrumb which does the same job.

Seomatic breadcrumb also doesn't include links not related to element pages. To learn how to add them to breadcrumb, head to Seomatic docs and look for "To entirely replace the existing BreadcrumbList on a page".

Why is such breacrumb component useful? #

Craft CMS allows us to build websites with URLs consisting of multiple segments. If your website has many types of categories and sections, your URL logic can get quite complicated. For example, let's consider such URL for article page:

category-list/1-level-category/2-level-category/article-page

Here are URL settings of various sections and categories needed to achieve such URL structure:

  • Article page section needs to have categoryField category field and such url setings: {categoryField.last.uri}/{slug}.
  • Categories have multiple levels, so for category group, URL settings would be: {parent.uri ?? craft.entries.section('category_list').one.uri}/{slug}.
  • Category list is single type section, so it only needs hardcoded entry title: category-list.

As you can see, these URL settings use element queries and conditional operators. We could reproduce all this logic within breadcrumb component in Twig. But we don't need to. Remember - Don't Repeat Yourself.

Breadcrumb plugin #

As an alternative to breadcrumb template component described in this article, you can use Breadcrumb plugin. It works pretty much the same while featuring few useful configuration options, like:

  • customising title of first breadcrumb link directing to homepage.
  • Setting maximum amount of breadcrumb links
  • Skipping specific segments

Just like with all plugins - before installing it consider if you really need it. Keep in mind that each plugin adds additional level of complexity to your website.

Article update history #

  • 1 july 2019 - breadcrumb homepage link is now created from homepage url or currentSite.baseUrl passed through alias function - to take into account situation when site base url is set to @web.
  • 15 august 2020 - added meta position tag to breadcrumb component.

TAGS:
#twig component #navigation
If you want to get latest updates on Craft CMS tutorials and components, follow me on Twitter or subscribe to RSS feed.
Quick links for this article:
Breadcrumb template component on github gists
Breadcrumb based on Seomatic on github gists
Articles on blog:
  • Frontend testing for Craft CMS websites with Codeception and Cypress
  • Building reactive Craft Commerce product page with Sprig plugin
  • Dynamically generated PDF attachments for Freeform submissions
  • Using template hooks in Craft CMS
  • Alpine JS modal component for Craft CMS
  • Using template UI elements to extend Craft CMS control panel
  • Matrix within a Matrix - possible solutions for Craft CMS
  • Universal email template for Craft CMS
  • Creating attributes table from entry fields in Craft CMS
  • Namespacing forms in Craft CMS
  • Creating map-based navigation for Craft CMS
  • Placeholder image macro for Craft CMS
  • Building AJAX contact form with Craft CMS
  • Using incognito field plugin for Craft CMS
  • Email footer creator made with Craft CMS
  • Infinite scrolling and lazy loading with Craft CMS
  • Using Javascript in Twig templates with Craft CMS
  • Twig templating tips and tricks for Craft CMS
  • Basic SEO functionality for Craft CMS
  • Working with dates in Craft CMS templates
  • Working with SVG images in Craft CMS templates
  • Responsive and lazy-loaded youtube videos with Craft CMS
  • Debugging and inspecting Twig templates in Craft CMS
  • Creating article excerpts with Twig component in Craft CMS
  • Adding favicons to Craft CMS website
  • Truncating text with Twig macros in Craft CMS
  • Universal language switcher for Craft CMS
  • Read time macro for Craft CMS
  • Using attr() function to render HTML attributes in Craft CMS
  • Building dynamic, AJAX based pagination for Craft CMS
  • How to add Disqus comments to Craft CMS website
  • Ellipsis pagination component for Craft CMS
  • Converting email addresses into links using Twig macro
  • Breadcrumb created from URL for Craft CMS
  • Best developer-oriented Craft CMS plugins
  • Search autocomplete component for Craft CMS
  • RSS feed - template component for Craft CMS
  • Testing emails sent by Craft CMS using Mailtrap
  • Quick edit link - Twig component for Craft CMS
  • Filtering entries in control panel using Searchit plugin
  • Fetching routes into Twig templates in Craft CMS


Frontend testing for Craft CMS websites with Codeception and Cypress

Building reactive Craft Commerce product page with Sprig plugin

Dynamically generated PDF attachments for Freeform submissions

Using template hooks in Craft CMS

Alpine JS modal component for Craft CMS

Using template UI elements to extend Craft CMS control panel

Matrix within a Matrix - possible solutions for Craft CMS

Universal email template for Craft CMS

Copyright ©2023 Piotr Pogorzelski