Overview

In this tutorial, we will go through how to set breadcrumbs in your Drupal 8 module. In Drupal 7 we were using drupal_set_breadcrumb(). In Drupal 8 drupal_set_breadcrumb() is removed and breadcrumbs are managed using the Breadcrumb service. There are only a few steps to do this:

  • Declare your service
  • Define a class implementing BreadcrumbBuilderInterface

This tutorial assumes that you have a working Drupal 8 module.

Drupal Core Breadcrumb

It is worth noting that the Drupal breadcrumb system analyzes each route created via module, creating a breadcrumb paths based on the parent-child relationships established in mymodule.routing.yml. It is therefore possible to have a breabcrumb ordered in unlimited levels simply by carefully assigning the relationships in the "path" entry of the mymodule.routing.yml files.
The following code interacts with Drupal's automatic breadcrumb management.

Declare Your Service

Your service gets declared in a file named mymodule.services.yml. The file should be placed immediately inside the module directory like ...modules/custom/mymodule/mymodule.services.yml. The minimum required contents include:

File: /mymodule/mymodule.services.yml

services:
 mymodule.breadcrumb:
  # The namespace + classname from your BreadcrumbBuilderInterface class
   class: Drupal\mymodule\Breadcrumb\myBreadcrumbBuilder
   
  # Priority determines the order in which Breadcrumb services run.
  tags:
    - { name: breadcrumb_builder, priority: 100 }

Define a class Implementing BreadcrumbBuilderInterface

  1. Create a .php file with the same name as your class like myBreadcrumbBuilder.php
  2. Place the file inside your module under /src/Breadcrumb like ...modules/custom/mymodule/src/Breadcrumb/myBreadcrumbBuilder.php
  3. Define a namespace. This namespace must match what you used for the class in mymodule.services.yml.
  4. Use namespaces. As a minimum, you will need:
    • use Drupal\Core\Breadcrumb\Breadcrumb;
    • use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
    • use Drupal\Core\Routing\RouteMatchInterface;
    • use Drupal\Core\Link;
  5. Define your class name. The name should match the filename and must match what came after the namespace in mymodule.services.yml. For example:
    class myBreadcrumbBuilder implements BreadcrumbBuilderInterface {
        
  6. Create a function applies() method to determine when your class will be used. Example below.
  7. Create a function build() method to determine what breadcrumbs your class will set. Example below.

File: /mymodule/src/Breadcrumb/myBreadcrumbBuilder.php

<?php
// Define the namespace for your class
namespace Drupal\mymodule\Breadcrumb;
 
// Use namespaces of classes that you need
use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Link ;
 
// Define your class and implement BreadcrumbBuilderInterface 
class myBreadcrumbBuilder implements BreadcrumbBuilderInterface {

  /**
   * {@inheritdoc}
   */
  public function applies(RouteMatchInterface $route_match) {
    // You can put any logic here. You must return a BOOLEAN TRUE or FALSE.
    //-----[ BEGIN example ]-----

    // Determine if the current page is a node page 
    $node = $route_match->getParameter('node');
    if ($node) {
      // You can do additional checks here for the node type, etc...
      return TRUE;
    }
    //-----[ END example ]-----

    // Still here? This does not apply.
    return FALSE;
 }
 
  /**
   * {@inheritdoc}
   */
  public function build(RouteMatchInterface $route_match) {
    // Define a new object of type Breadcrumb
    $breadcrumb = new Breadcrumb();

    // You can put any logic here to build out your breadcrumb.
    //-----[ BEGIN example ]-----
    // Add a link to the homepage as our first crumb.
    $breadcrumb->addLink(Link::createFromRoute('Home', '<front>'));
 
    // Get the node for the current page
    $node = $route_match->getParameter('node');

    // Special handling based on node type aka bundle.
    // NOTE use of the Link class.
    switch ($node->bundle()) {
      case 'project':
        $breadcrumb->addLink(Link::createFromRoute('Project List Page', 'view.article.page_1'));
        break;
 
      case 'article':
        $breadcrumb->addLink(Link::createFromRoute('Article List Page', 'view.articles.page_1'));
        break;
    }
    //-----[ END example ]-----

 
    // Don't forget to add cache control by a route.
    // Otherwise all pages will have the same breadcrumb.
    $breadcrumb->addCacheContexts(['route']);
 
    // Return object of type breadcrumb.
    return $breadcrumb;
  }
 
}

Credits

I learned from the following blog posts: