Breadcrumb created from URL for Craft CMS
Tags: craftcms, cms, breadcrumbs, webdev
Published: 2019-07-17 17:23:25

---
title: Breadcrumb created from URL for Craft CMS
published: true
description: Breadcrumb component automatically created from URL. Each URL segment is represented by a single breadcrumb link.
tags: craftcms,cms,breadcrumbs,webdev
canonical_url: http://craftsnippets.com/articles/breadcrumb-created-from-url-for-craft-cms
series: Craft Snippets
cover_image: http://craftsnippets.com/imager/postimages/13/bread_a711e44055a22ab7be0e2b18ebea89a1.jpg
---

This [article](http://craftsnippets.com/articles/breadcrumb-created-from-url-for-craft-cms) was originally posted on [craftsnippets.com](http://craftsnippets.com?s=dt), blog about Craft CMS. Head there for more Craft-related articles and ready to use template components.

---------------

## 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](https://bulma.io/documentation/components/breadcrumb/).
* It has google [structured data attributes](https://developers.google.com/search/docs/data-types/breadcrumb) to improve your SEO.
* It also has proper [ARIA](https://www.w3.org/TR/wai-aria-practices/examples/breadcrumb/index.html) attributes. ARIA stands for [Accessible Rich Internet Applications](https://www.sitepoint.com/how-to-use-aria-effectively-with-html5/)  - 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:

```twig
{# v3 #}
{# 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>
            </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](https://docs.craftcms.com/v3/extend/element-types.html) 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](https://docs.craftcms.com/v3/static-translations.html).

## Breadcrumb with Seomatic

If you are already using **[Seomatic](https://plugins.craftcms.com/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:

```twig
{% 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>
            </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](https://github.com/nystudio107/craft-seomatic) 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](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself).

## Breadcrumb plugin

As an alternative to breadcrumb template component described in this article, you can use [Breadcrumb plugin](https://github.com/youandmedigital/craft-breadcrumb). 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`.