Core Breadcrumb Architecture
When to Use
Understanding this section is required before writing any custom builder or alter hook. The
BreadcrumbManageris the chain dispatcher; every breadcrumb request routes through it.
Decision
| Component | Role | Key method |
|---|---|---|
BreadcrumbBuilderInterface |
Contract all builders implement | applies(), build() |
BreadcrumbManager |
Chain dispatcher, sorted by priority | build() calls getSortedBuilders(), iterates, takes first applies() === TRUE |
Breadcrumb |
Value object: links + cache metadata | addLink(), addCacheContexts(), addCacheableDependency() |
BreadcrumbPreprocess |
Template preprocessor | Converts Link objects to [text, url] arrays for Twig |
hook_system_breadcrumb_alter |
Post-build hook | Fires after winning builder; receives Breadcrumb, RouteMatchInterface, $context |
Pattern
How BreadcrumbManager::build() resolves the winning builder:
// BreadcrumbManager::build() — simplified
$cacheable_metadata = new CacheableMetadata();
$breadcrumb = new Breadcrumb();
foreach ($this->getSortedBuilders() as $builder) {
if (!$builder->applies($route_match, $cacheable_metadata)) {
continue;
}
$breadcrumb = $builder->build($route_match);
$context['builder'] = $builder;
break;
}
$breadcrumb->addCacheableDependency($cacheable_metadata); // merge applies() metadata
$this->moduleHandler->alter('system_breadcrumb', $breadcrumb, $route_match, $context);
return $breadcrumb;
The Breadcrumb class uses RefinableCacheableDependencyTrait, implementing RefinableCacheableDependencyInterface. Any cacheability added in applies() is automatically merged into the returned breadcrumb — so do not duplicate it in build().
Service registration: builders are tagged with breadcrumb_builder in *.services.yml. The RegisterBreadcrumbBuilderPass compiler pass collects them by priority and calls BreadcrumbManager::addBuilder().
Priority order: higher number = higher priority = checked first. The first builder where applies() returns TRUE wins; subsequent builders are never called.
Common Mistakes
- Adding cache metadata in both
applies()andbuild()— it is merged automatically; double-adding causes no error but is redundant - Returning
NULLfrombuild()— the manager throwsUnexpectedValueException; always return aBreadcrumbinstance - Assuming all builders run — only the winning builder's
build()is called; losers only haveapplies()called
See Also
- Builder priorities → Core Breadcrumb Builders
- Writing your own builder → Custom Breadcrumb Builder
- Reference:
core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php - Reference:
core/lib/Drupal/Core/Breadcrumb/Breadcrumb.php