The breadcrumb code seems to gather everything from the URL, as it should. But in my module, the other entries used in the URL path do not represent somewhere where the user can go. They instead pass information from my form to a controller object.

Is there a way to remove these entries in the breadcrumb from my modules' controller object?

2 Answers 2


To customize your Breadcrumbs you need to take full control of the Breadcrumb for that particular route with your own Breadcrumb Builder service.

To do this, you need to create an entry in your module/profile services file:

e.g., mymodule/mymodule.services.yml

    class: Drupal\mymodule\MyModuleBreadcrumbBuilder
      - { name: breadcrumb_builder, priority: 100 }

You then need to create your Breadcrumb Builder class

e.g. mymodule/src/MyModuleBreadcrumbBuilder.php


 * @file
 * Contains Drupal\mymodule\MyModuleBreadcrumbBuilder.

namespace Drupal\mymodule;

use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;

 * Class MyModuleBreadcrumbBuilder.
 * @package Drupal\mymodule
class MyModuleBreadcrumbBuilder implements BreadcrumbBuilderInterface {

   * {@inheritdoc}
  public function applies(RouteMatchInterface $route_match) {
    $route = $route_match->getCurrentRouteMatch();

    // Add conditionals that return TRUE when the current route should have it's
    // breadcrumb handled here.
    return FALSE;

   * {@inheritdoc}
  public function build(RouteMatchInterface $route_match) {
    $breadcrumb = new Breadcrumb();

    // Add logic here that builds up the breadcrumbs based on desired behaviour.
    $links[] = Link::createFromRoute(t('Home'), '<front>');


    return $breadcrumb;


The applies method is where you register this Breadcrumb Builder to be used for the particular route, so build a conditional there that matches your route(s) and returns TRUE.

The build method is where you build your Breadcrumb for any routes that applies returns TRUE, so if you are dealing with multiple routes you will need to build the appropriate logic here to build said Breadcrumbs.

I recommend reading through Palantir's blog post for more background and details: https://www.palantir.net/blog/d8ftw-breadcrumbs-work

  • 1
    Whats the priority for please? Commented Nov 23, 2018 at 22:49
  • 2
    Priority is explained here: valuebound.com/resources/blog/how-to-create-breadcrumb-drupal-8 Commented Nov 24, 2018 at 0:49
  • 1
    +1 upvote on both question and answer. This question/answer solved my problem whereby, after using redirect in a form submit, I got superfluous breadcrumbs (e.g. Home / Admin / Same Link / Same Link / Same Link / Same Link). With this solution I was able to correct this. Worth knowing that if you want to rebuild breadcrumb trail that includes admin, the route name is system.admin as defined in web/core/modules/system/system.links.menu.yml and you can use this route name for building your breadcrumb, in the build method as the above answer describes above. Commented Nov 24, 2018 at 0:52

This was a very interesting problem. I ran into this today while trying to set up custom controller to display a user dashboard. All the posts I saw was about overriding the BreadCrumbBuild system. I wanted to see what the breadcrumb builder was doing in the first place.

The default breadcrumb processing happens through this logic in the


file. This is the logic used for creating the breadcrumb.

public function build(RouteMatchInterface $route_match) {
    $breadcrumb = new Breadcrumb();
    $links = [];

    // Add the url.path.parent cache context. This code ignores the last path
    // part so the result only depends on the path parents.
    $breadcrumb->addCacheContexts(['url.path.parent', 'url.path.is_front']);

    // Do not display a breadcrumb on the frontpage.
    if ($this->pathMatcher->isFrontPage()) {
      return $breadcrumb;

    // General path-based breadcrumbs. Use the actual request path, prior to
    // resolving path aliases, so the breadcrumb can be defined by simply
    // creating a hierarchy of path aliases.
    $path = trim($this->context->getPathInfo(), '/');
    $path_elements = explode('/', $path);
    $exclude = [];
    // Don't show a link to the front-page path.
    $front = $this->config->get('page.front');
    $exclude[$front] = TRUE;
    // /user is just a redirect, so skip it.
    // @todo Find a better way to deal with /user.
    $exclude['/user'] = TRUE;
    while (count($path_elements) > 1) {
      // Copy the path elements for up-casting.
      $route_request = $this->getRequestForPath('/' . implode('/', $path_elements), $exclude);
      if ($route_request) {
        $route_match = RouteMatch::createFromRequest($route_request);
        $access = $this->accessManager->check($route_match, $this->currentUser, NULL, TRUE);
        // The set of breadcrumb links depends on the access result, so merge
        // the access result's cacheability metadata.
        $breadcrumb = $breadcrumb->addCacheableDependency($access);
        if ($access->isAllowed()) {
          $title = $this->titleResolver->getTitle($route_request, $route_match->getRouteObject());
          if (!isset($title)) {
            // Fallback to using the raw path component as the title if the
            // route is missing a _title or _title_callback attribute.
            $title = str_replace(['-', '_'], ' ', Unicode::ucfirst(end($path_elements)));
          $url = Url::fromRouteMatch($route_match);
          $links[] = new Link($title, $url);

    // Add the Home link.
    $links[] = Link::createFromRoute($this->t('Home'), '<front>');

    return $breadcrumb->setLinks(array_reverse($links));

So what it does is it trims the full URL by removing substrings after each of the path separators (/) and then checking if the url created corresponds to a valid route and if the route is accessible to the user and then creates the breadcrumb. So if the subsequently generated trimmed versions of the URL is not to be accessed by the user then there is a permissions issue for that route. If they are not meaningful routes they wouldn't be shown.

I ran into the issue while using a custom title callback for the controller. I had used the same callback for multiple routes from the same controller. However I was using the path of the current request to create the title. So the breadcrumbs were all created with the current page's title. I fixed the issue by adding the RouteMatchInterface as an argument to the _title_callback. This was available as a typehinted argument for the Controller and the title_callback

See: https://www.drupal.org/docs/8/api/routing-system/parameters-in-routes/using-parameters-in-routes#s-typehinted-parameters

Hope this helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.