tabuna/breadcrumbs
Laravel breadcrumbs made easy: define breadcrumb trails right in your route definitions with a fluent API (parent/push), automatic route detection, and support for request parameters and model binding to generate consistent navigation across your app.
## Getting Started
### **Minimal Setup**
1. **Installation**
```bash
composer require tabuna/breadcrumbs:^5.0
Publish the config (optional):
php artisan vendor:publish --provider="Tabuna\Breadcrumbs\BreadcrumbsServiceProvider"
Basic Usage (Updated for 5.0) Define a breadcrumb trail using named parameter resolution in a controller or service:
use Tabuna\Breadcrumbs\Trail;
use Tabuna\Breadcrumbs\BreadcrumbsGenerator;
public function show(Post $post, BreadcrumbsGenerator $breadcrumbs)
{
$breadcrumbs->push('Home', route('home'));
$breadcrumbs->push('Blog', route('posts.index'));
$breadcrumbs->push($post->title, route('posts.show', $post));
}
Alternative (Closure Syntax):
$breadcrumbs->breadcrumbs(fn (Trail $trail, Post $post) =>
$trail->push($post->title, route('posts.show', $post->id))
);
Display in Blade
@include('breadcrumbs::breadcrumbs')
Or manually:
<div class="breadcrumbs">
{!! $breadcrumbs->render() !!}
</div>
// app/Http/Controllers/ProductController.php
public function show(Product $product, BreadcrumbsGenerator $breadcrumbs)
{
$breadcrumbs->parent('products.index'); // Reuse existing trail
$breadcrumbs->push($product->name, route('products.show', $product));
}
Blade:
@breadcrumbs(['Home', 'Products', 'Product Name'])
Group Trails by Route/Controller (Closure-Based) Use named parameter resolution in closures:
// app/Breadcrumbs/ProductTrail.php
namespace App\Breadcrumbs;
use Tabuna\Breadcrumbs\Trail;
class ProductTrail
{
public function __invoke(Trail $trail, Product $product)
{
$trail->parent('products.index');
$trail->push($product->name, route('products.show', $product));
}
}
Usage:
$breadcrumbs->breadcrumbs(ProductTrail::class);
Reuse Trails with parent()
$breadcrumbs->parent('products.index');
$breadcrumbs->push('Edit', route('products.edit', $product));
Pass Models/Variables (Named Resolution)
$breadcrumbs->breadcrumbs(fn (Trail $trail, User $user) =>
$trail->push('User Posts', route('users.posts', $user))
);
Blade (with @breadcrumbs directive):
@breadcrumbs(['Home', 'Users', 'John Doe\'s Posts'])
Localization
$breadcrumbs->push(__('Products'), route('products.index'));
Middleware for Global Trails (5.0 Compatible)
// app/Http/Middleware/SetBreadcrumbs.php
public function handle($request, Closure $next)
{
$breadcrumbs = app(BreadcrumbsGenerator::class);
$breadcrumbs->breadcrumbs(fn (Trail $trail) =>
$trail->push('Home', route('home'))
);
return $next($request);
}
API Responses (Updated)
return response()->json([
'data' => $product,
'breadcrumbs' => $breadcrumbs->getTrail()->toArray(),
]);
Override Default View
php artisan vendor:publish --tag=breadcrumbs-views
Modify resources/views/vendor/breadcrumbs/breadcrumbs.blade.php.
Custom Separator/Styling
$breadcrumbs->setSeparator('<span class="divider">/</span>');
$breadcrumbs->setWrapperTag('nav', ['aria-label' => 'Breadcrumb']);
Trail Not Rendering
@include('breadcrumbs::breadcrumbs') or manually rendering $breadcrumbs->render().Trail parameter explicitly (e.g., fn (Trail $trail) => ...).Duplicate Trails
$breadcrumbs->push() without clearing or using parent().$breadcrumbs->clear() or $breadcrumbs->parent('trail.name') to reset.Route Not Found
route() with undefined named routes.url()->route()).Closure Parameter Mismatch (5.0)
Trail class signature.fn (Trail $trail, Model $model) => ... for consistency.dd($breadcrumbs->getTrail()->toArray());
web middleware group).$breadcrumbs->breadcrumbs(fn (Trail $trail, Post $post) => ...);
Error: If $post is missing, Laravel will throw a BindingResolutionException.Default Separator
Override in config/breadcrumbs.php:
'separator' => '»',
Trail Naming
Use kebab-case for trail names (e.g., products.index) to avoid conflicts.
Caching Disable caching if trails are dynamic:
$breadcrumbs->disableCache();
Closure-Based Trails (5.0)
Trail::call() is now private; use closures for custom logic.$breadcrumbs->breadcrumbs(...) returns the generator for chaining.Custom Trail Providers (Closure-Based)
// app/Providers/BreadcrumbsServiceProvider.php
public function boot()
{
Breadcrumbs::provider(function (BreadcrumbsGenerator $breadcrumbs) {
$breadcrumbs->breadcrumbs(fn (Trail $trail) =>
$trail->push('Global Trail', route('home'))
);
});
}
Event-Based Trails (Updated)
Trigger trails via events (e.g., ModelRetrieved):
event(new ModelRetrieved($product));
// In listener:
$breadcrumbs->breadcrumbs(fn (Trail $trail, Product $product) =>
$trail->push($product->name, route('products.show', $product))
);
Third-Party Integration (Unchanged)
breadcrumbs::breadcrumbs in toolbars.public $breadcrumbs;
protected $listeners = ['updateBreadcrumbs'];
cache()->remember() for trails that rarely change:
$trail = cache()->remember("breadcrumbs.home", now()->addHours(1), function () {
return Trail::create('Home')->push('Home', route('home'));
});
Trail::call() is Private
Direct calls to Trail::call() will fail. Use closures instead:
// Old (deprecated):
$trail->call(ProductTrail::class, $product);
// New (5.0):
$breadcrumbs->breadcrumbs(ProductTrail::class);
Generator Class Renamed `
How can I help you explore Laravel packages today?