Installation
composer require arjanhulst/breadcrumbs-bundle
Register the bundle in config/bundles.php:
return [
// ...
ArjanHulst\BreadcrumbsBundle\ArjanHulstBreadcrumbsBundle::class => ['all' => true],
];
Enable Annotations
Ensure annotations is enabled in config/packages/framework.yaml:
framework:
annotations: true
First Use Case: Basic Crumb Add an annotation to a controller method:
use ArjanHulst\BreadcrumbsBundle\Annotation\Breadcrumb;
class ProductController extends AbstractController
{
/**
* @Breadcrumb("Products")
*/
public function index(): Response
{
return $this->render('product/index.html.twig');
}
/**
* @Breadcrumb("Product", route="product_show", routeParameters={"id": "$id"})
*/
public function show(Product $product): Response
{
return $this->render('product/show.html.twig', ['product' => $product]);
}
}
Display in Twig Add to your template:
{{ breadcrumbs() }}
Route-Based Crumbs
Use route and routeParameters to dynamically link crumbs:
/**
* @Breadcrumb("Category", route="category_show", routeParameters={"slug": "$slug"})
*/
public function show(Category $category): Response
{
// ...
}
Entity-Based Crumbs Leverage Doctrine entities for dynamic labels:
/**
* @Breadcrumb(entity="product", property="name", route="product_show", routeParameters={"id": "$id"})
*/
public function show(Product $product): Response
{
// ...
}
Parent-Child Relationships Chain crumbs to reflect hierarchy:
/**
* @Breadcrumb("Home")
* @Breadcrumb("Products")
* @Breadcrumb(entity="category", property="name", route="category_show", routeParameters={"slug": "$slug"})
*/
public function index(Category $category): Response
{
// ...
}
Override Default Renderer Create a custom Twig extension:
// src/Twig/BreadcrumbExtension.php
class BreadcrumbExtension extends \Twig\Extension\AbstractExtension
{
public function getFunctions()
{
return [
new \Twig\TwigFunction('custom_breadcrumbs', [$this, 'renderBreadcrumbs']),
];
}
public function renderBreadcrumbs()
{
// Custom logic (e.g., add icons, modify separators)
return $this->renderBreadcrumbsTemplate();
}
}
Dynamic Crumbs via Services
Inject the BreadcrumbService to build crumbs programmatically:
public function __construct(private BreadcrumbService $breadcrumbService) {}
public function someAction(): Response
{
$this->breadcrumbService->add('Dynamic Crumb', ['route' => 'some_route']);
return $this->render('template.html.twig');
}
Prepend Crumbs in Events
Use the BreadcrumbEvent in Symfony events:
# config/services.yaml
services:
App\EventListener\BreadcrumbListener:
tags:
- { name: kernel.event_listener, event: breadcrumb, method: onBreadcrumb }
class BreadcrumbListener
{
public function onBreadcrumb(BreadcrumbEvent $event)
{
if ($event->getRoute() === 'homepage') {
$event->add('Homepage', ['route' => 'home']);
}
}
}
Annotation Caching Clear cache after adding new annotations:
php bin/console cache:clear
Route Parameter Mismatches
Ensure routeParameters match the actual route definition. Use $id for dynamic segments:
// Correct:
routeParameters={"id": "$id"}
// Incorrect (will fail):
routeParameters={"id": "123"}
Entity Property Access
Verify property in @Breadcrumb exists in the entity. Use getter methods if needed:
// Entity:
public function getFullName(): string { return $this->firstName . ' ' . $this->lastName; }
// Annotation:
property="fullName" // Calls getFullName()
Twig Template Not Found
If {{ breadcrumbs() }} fails, ensure the Twig template exists at:
templates/bundles/ArjanHulstBreadcrumbs/breadcrumbs.html.twig.
Override it in your project if needed.
Dump Crumbs
Use the debug:breadcrumbs command to inspect active crumbs:
php bin/console debug:breadcrumbs
Check Event Dispatching
If crumbs aren’t rendering, verify the BreadcrumbEvent is fired. Add a listener to log:
public function onBreadcrumb(BreadcrumbEvent $event)
{
error_log('Breadcrumb added: ' . print_r($event->getCrumb(), true));
}
Custom Crumb Types
Extend the CrumbInterface to add metadata (e.g., icons, badges):
class CustomCrumb implements CrumbInterface
{
private string $icon;
public function setIcon(string $icon): self { $this->icon = $icon; return $this; }
public function getIcon(): string { return $this->icon; }
}
Modify Crumb Storage
Override the BreadcrumbStorage service to persist crumbs across requests (e.g., for AJAX):
services:
ArjanHulst\BreadcrumbsBundle\Storage\BreadcrumbStorage:
arguments:
$storage: '@session' # Use session instead of default
Localization Use translation keys in annotations:
/**
* @Breadcrumb(translation="breadcrumbs.products")
*/
Define translations in translations/messages.en.yaml:
breadcrumbs:
products: "Products"
repositoryMethod to fetch entities only when needed:
/**
* @Breadcrumb(entity="product", repositoryMethod="findBySlug", route="product_show", routeParameters={"slug": "$slug"})
*/
How can I help you explore Laravel packages today?