PHPackages                             daikazu/breadcrumbs - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. daikazu/breadcrumbs

ActiveLibrary

daikazu/breadcrumbs
===================

Route-aware breadcrumb management for Laravel

v1.0.0(1mo ago)06MITPHPPHP ^8.4CI passing

Since Mar 19Pushed 1mo agoCompare

[ Source](https://github.com/daikazu/breadcrumbs)[ Packagist](https://packagist.org/packages/daikazu/breadcrumbs)[ Docs](https://github.com/daikazu/breadcrumbs)[ GitHub Sponsors](https://github.com/Daikazu)[ RSS](/packages/daikazu-breadcrumbs/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (15)Versions (2)Used By (0)

  ![Logo for Breadcrumbs](art/header-light.png)Route-aware breadcrumb management for Laravel
=============================================

[](#route-aware-breadcrumb-management-for-laravel)

[![Latest Version on Packagist](https://camo.githubusercontent.com/0c5c9fe5c5f321f8f4982a98bf23c8d0bc28785a8fff594a01c860e2dcd9ad4f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6461696b617a752f62726561646372756d62732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/daikazu/breadcrumbs)[![GitHub Tests Action Status](https://camo.githubusercontent.com/ee8a69b1f143938486b6a20b5686c575cd52955f3b074d98786bff5ad56806bb/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6461696b617a752f62726561646372756d62732f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/daikazu/breadcrumbs/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/e50351111eb7dd9995bfbd42e337be1778595c38c0cd9373b07c42873409a223/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6461696b617a752f62726561646372756d62732f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/daikazu/breadcrumbs/actions?query=workflow%3A%22Fix+PHP+code+style+issues%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/b366ad83ea0dbeb01ada07a7790b1745f9fa3621fd67dfc6e33efae79c4d8c71/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6461696b617a752f62726561646372756d62732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/daikazu/breadcrumbs)

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require daikazu/breadcrumbs
```

You can publish the config file with:

```
php artisan vendor:publish --tag="breadcrumbs-config"
```

Optionally, you can publish the views using

```
php artisan vendor:publish --tag="breadcrumbs-views"
```

Usage
-----

[](#usage)

- [Quick Start](#quick-start)
- [Defining Breadcrumbs](#defining-breadcrumbs)
- [Parent Chaining](#parent-chaining)
- [Route Model Binding](#route-model-binding)
- [HasBreadcrumb Interface](#hasbreadcrumb-interface)
- [Rendering](#rendering)
- [Trail::home()](#trailhome)
- [Trail::fromArray() / Trail::fromJson()](#trailfromarray--trailfromjson)
- [Truncation](#truncation)
- [Caching](#caching)
- [Middleware](#middleware)
- [Livewire Integration](#livewire-integration)
- [Configuration](#configuration)

---

### Quick Start

[](#quick-start)

Register a breadcrumb definition in a service provider, then render with the Blade component.

**1. Register definitions** — create a `routes/breadcrumbs.php` file and the package will auto-load it. Alternatively, register definitions directly in `AppServiceProvider::boot()`. The file path is configurable via the `definition_file` config key.

```
// app/Providers/AppServiceProvider.php
use Daikazu\Breadcrumbs\Facades\Breadcrumbs;
use Daikazu\Breadcrumbs\Trail;

public function boot(): void
{
    Breadcrumbs::for('home', function (Trail $trail) {
        $trail->push('Home', route('home'));
    });

    Breadcrumbs::for('about', function (Trail $trail) {
        $trail->parent('home')->push('About', route('about'));
    });
}
```

**2. Render in a Blade layout:**

```

```

The component resolves the current route automatically and renders nothing if no matching definition exists.

---

### Defining Breadcrumbs

[](#defining-breadcrumbs)

Use `Breadcrumbs::for()` to register a definition keyed to a route name. The closure receives a `Trail` instance as its first argument.

```
use Daikazu\Breadcrumbs\Facades\Breadcrumbs;
use Daikazu\Breadcrumbs\Trail;

Breadcrumbs::for('products.index', function (Trail $trail) {
    $trail->push('Home', route('home'))
          ->push('Products', route('products.index'));
});
```

**Trail methods:**

MethodDescription`push(string $label, string $url = '', array $data = [])`Appends a crumb to the end of the trail`prepend(string $label, string $url = '', array $data = [])`Inserts a crumb at the beginning of the trail`parent(string $routeName, mixed ...$params)`Resolves a parent definition and prepends its crumbs`home(?string $label, ?string $url)`Shorthand for the home crumb using config defaultsThe `$data` array on `push` and `prepend` accepts arbitrary metadata that your view can use — icon names, CSS classes, flags, and so on.

```
$trail->push('Products', route('products.index'), ['icon' => 'shopping-bag']);
```

---

### Parent Chaining

[](#parent-chaining)

`$trail->parent()` resolves another registered definition and prepends its crumbs to the current trail. This composes full paths from independent definitions without duplication.

```
// Home
Breadcrumbs::for('home', function (Trail $trail) {
    $trail->push('Home', route('home'));
});

// Products index
Breadcrumbs::for('products.index', function (Trail $trail) {
    $trail->parent('home')
          ->push('Products', route('products.index'));
});

// Individual product — chains three levels deep
Breadcrumbs::for('products.show', function (Trail $trail, Product $product) {
    $trail->parent('products.index')
          ->push($product->name, route('products.show', $product));
});
```

Resolving `products.show` produces: **Home &gt; Products &gt; \[product name\]**

Each level only defines its own crumb; the chain is assembled at resolve time.

---

### Route Model Binding

[](#route-model-binding)

When `Breadcrumbs::current()` is called, the package inspects the current route's bound parameters via `Route::current()->parameters()` and injects them into the closure automatically, matching by type-hint first, then by parameter name.

```
Breadcrumbs::for('products.show', function (Trail $trail, Product $product) {
    $trail->parent('products.index')
          ->push($product->name, route('products.show', $product));
});
```

If the route has `{product}` bound to a `Product` model instance, it is injected without any extra configuration. The resolution mirrors Laravel's own dependency injection behavior.

Routes with multiple bound models are also supported:

```
Breadcrumbs::for('orders.items.show', function (Trail $trail, Order $order, OrderItem $item) {
    $trail->parent('orders.show', $order)
          ->push("Item #{$item->id}", route('orders.items.show', [$order, $item]));
});
```

---

### HasBreadcrumb Interface

[](#hasbreadcrumb-interface)

As an alternative to registering a closure, an Eloquent model can own its own breadcrumb definition by implementing `Daikazu\Breadcrumbs\Contracts\HasBreadcrumb`.

```
use Daikazu\Breadcrumbs\Contracts\HasBreadcrumb;
use Daikazu\Breadcrumbs\Trail;

class Product extends Model implements HasBreadcrumb
{
    public function toBreadcrumb(Trail $trail): void
    {
        $trail->parent('products.index')
              ->push($this->name, route('products.show', $this));
    }
}
```

No `Breadcrumbs::for()` call is needed. When the route binds a `Product`, the manager calls `toBreadcrumb()` automatically.

**Resolution priority** when no registered closure exists for the current route:

1. Registered closure via `Breadcrumbs::for()`
2. Bound model implementing `HasBreadcrumb`
3. `MissingBreadcrumbException` or silent empty trail

A registered closure always wins over `HasBreadcrumb`. This lets you override a model's default definition on a per-route basis.

---

### Rendering

[](#rendering)

#### Blade component

[](#blade-component)

```

```

The component resolves the current route. It calls `shouldRender()` internally and produces no output when the trail is empty, so it is safe to include unconditionally in layouts.

To render breadcrumbs for a specific route instead of the current one:

```

```

Pass route parameters when the target route requires them:

```

```

#### JSON-LD schema directive

[](#json-ld-schema-directive)

Place `@breadcrumbsSchema` anywhere in your layout to output a `` tag with a valid `BreadcrumbList` schema. The head is the recommended location.

```

    My Site
    @breadcrumbsSchema

```

The directive renders nothing if no breadcrumb trail can be resolved, so no empty script tags are output. The schema uses `BreadcrumbTrail::toSchema()` internally and follows the schema.org `BreadcrumbList` specification.

#### Available views

[](#available-views)

Two built-in views ship with the package:

View nameDescription`breadcrumbs::tailwind`Tailwind CSS — default`breadcrumbs::bootstrap5`Bootstrap 5Switch the active view in `config/breadcrumbs.php`:

```
'view' => 'breadcrumbs::bootstrap5',
```

#### Publishing and customizing views

[](#publishing-and-customizing-views)

```
php artisan vendor:publish --tag="breadcrumbs-views"
```

This copies the views to `resources/views/vendor/breadcrumbs/`. Edit them freely. The `schema.blade.php` partial that renders the JSON-LD script tag is structural and is not published.

After publishing, set `view` in config to point to your custom view:

```
'view' => 'breadcrumbs::tailwind', // or your own view name after customizing
```

Each view receives a `$breadcrumbs` variable — an instance of `BreadcrumbTrail`, which extends `Illuminate\Support\Collection` of `Crumb` objects.

```
@foreach ($breadcrumbs as $crumb)
    {{-- $crumb->label, $crumb->url, $crumb->active, $crumb->data --}}
@endforeach
```

---

### Trail::home()

[](#trailhome)

`Trail::home()` is a shorthand that prepends the home crumb using `home_label` and `home_route` from the config. It is equivalent to calling `$trail->prepend(config('breadcrumbs.home_label'), route(config('breadcrumbs.home_route')))`.

```
Breadcrumbs::for('about', function (Trail $trail) {
    $trail->home()->push('About', route('about'));
});
```

Override either value inline:

```
$trail->home('Start', route('dashboard'));
```

Both parameters are optional. Passing `null` (or omitting them) falls back to config values.

---

### Trail::fromArray() / Trail::fromJson()

[](#trailfromarray--trailfromjson)

These static factory methods build a `Trail` directly from a data source, bypassing the resolver. They are intended for headless setups, CMS-driven pages, and API responses where breadcrumb data arrives pre-assembled.

```
use Daikazu\Breadcrumbs\Trail;

$trail = Trail::fromArray([
    ['label' => 'Home', 'url' => 'https://example.com'],
    ['label' => 'Blog', 'url' => 'https://example.com/blog'],
    ['label' => 'My Post', 'url' => 'https://example.com/blog/my-post'],
]);
```

```
$trail = Trail::fromJson('[
    {"label": "Home", "url": "https://example.com"},
    {"label": "Blog", "url": "https://example.com/blog"},
    {"label": "My Post", "url": "https://example.com/blog/my-post"}
]');
```

Each entry requires a `label` key. The `url` key is optional.

`Trail::fromJson()` throws `Daikazu\Breadcrumbs\Exceptions\InvalidTrailDataException` if the input is not valid JSON or does not match the expected `[{label, url}]` shape.

---

### Truncation

[](#truncation)

`BreadcrumbTrail::truncate()` collapses a long trail into a fixed maximum number of items by replacing the middle crumbs with an ellipsis placeholder. It always preserves the first crumb and the last crumb.

```
// Resolves: Home > Clothing > Men > Tops > T-Shirts > Plain Tees
$trail = Breadcrumbs::generate('products.index', $category);

// Truncate to 4 items: Home > … > T-Shirts > Plain Tees
$truncated = $trail->truncate(4);
```

The ellipsis crumb has an empty URL and `['truncated' => true]` in its `data` array so views can style it differently (for example, rendering it as a non-link).

```
@foreach ($breadcrumbs->truncate(4) as $crumb)
    @if ($crumb->data['truncated'] ?? false)
        {{ $crumb->label }}
    @else
        {{ $crumb->label }}
    @endif
@endforeach
```

`truncate()` returns a new `BreadcrumbTrail` instance and does not mutate the original. When the trail length is already within `$maxItems`, the original items are returned unchanged. The `$maxItems` argument must be at least 3 (first + ellipsis + last); values below 3 are treated as a no-op.

The default ellipsis label is `…`. Pass a custom string as the second argument:

```
$trail->truncate(4, '...');
```

---

### Caching

[](#caching)

For routes whose breadcrumb resolution involves database queries — such as loading a category ancestor chain — enable per-definition caching by chaining `->cache()` on the return value of `Breadcrumbs::for()`.

```
Breadcrumbs::for('categories.show', function (Trail $trail, Category $category) {
    $trail->parent('categories.index')
          ->push($category->name, route('categories.show', $category));
})->cache(ttl: 3600, tags: ['breadcrumbs', 'categories']);
```

ParameterTypeDescription`ttl``int`Cache lifetime in seconds. Required.`tags``array|null`Cache tags for tagged drivers. Defaults to the `cache_tags` config value (`['breadcrumbs']`).Cache keys are scoped per route and per model instance using model class names and primary keys — not `serialize()` — so loaded relations do not affect cache hits:

```
breadcrumbs:categories.show:{md5 hash}

```

The cache store used is whatever is configured in `breadcrumbs.cache_store` (defaults to the application's default store). Cache tags require a driver that supports them (Redis, Memcached). Do not set tags on a file or database cache driver.

#### Cache invalidation with BreadcrumbCacheObserver

[](#cache-invalidation-with-breadcrumbcacheobserver)

The package ships a `BreadcrumbCacheObserver` that flushes breadcrumb cache entries by tag when a model fires `saved` or `deleted` events. Register it yourself in a service provider — the package does not auto-register it.

```
use Daikazu\Breadcrumbs\BreadcrumbCacheObserver;

Category::observe(new BreadcrumbCacheObserver(['breadcrumbs', 'categories']));
```

The observer only flushes by tag, so the cache driver must support tagging. The tags passed to the observer should match those used in the `->cache()` call.

---

### Middleware

[](#middleware)

`Daikazu\Breadcrumbs\Middleware\SetBreadcrumbs` resolves `Breadcrumbs::current()` after the controller has executed and shares the result as `$breadcrumbs` with all views via `View::share()`. This is a convenience; the package functions without it.

Register it in your application's middleware stack:

```
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->web(append: [
        \Daikazu\Breadcrumbs\Middleware\SetBreadcrumbs::class,
    ]);
})
```

Or apply it to specific route groups:

```
Route::middleware(\Daikazu\Breadcrumbs\Middleware\SetBreadcrumbs::class)
    ->group(base_path('routes/web.php'));
```

With the middleware active, `$breadcrumbs` is available in every view without calling `Breadcrumbs::current()` manually.

---

### Livewire Integration

[](#livewire-integration)

When `livewire/livewire` is installed and `breadcrumbs.livewire` is `true`, the package registers a Livewire component in place of the standard Blade component. The Livewire component re-resolves the breadcrumb trail on `wire:navigate` page transitions without a full reload.

Enable it in `config/breadcrumbs.php`:

```
'livewire' => env('BREADCRUMBS_LIVEWIRE', true),
```

Or via environment variable:

```
BREADCRUMBS_LIVEWIRE=true

```

The `` tag in your layout works without modification. When Livewire is not installed or `breadcrumbs.livewire` is `false`, the standard Blade component is used. No Livewire dependency is introduced for non-Livewire applications.

Internally, the component listens for the `breadcrumbs:refresh` browser event triggered by Livewire's `navigate` hook:

```
document.addEventListener('livewire:navigate', () => {
    Livewire.dispatch('breadcrumbs:refresh');
});
```

This script is only emitted when `breadcrumbs.livewire` is `true`.

---

### Laravel Octane

[](#laravel-octane)

If you run Laravel Octane (or any long-lived process), call `Breadcrumbs::flush()` between requests to clear registered definitions and prevent state leaking across requests:

```
// app/Providers/AppServiceProvider.php
use Laravel\Octane\Events\RequestTerminated;

public function boot(): void
{
    $this->app['events']->listen(RequestTerminated::class, function () {
        app('breadcrumbs')->flush();
    });
}
```

---

### Configuration

[](#configuration)

Publish the config file to customize defaults:

```
php artisan vendor:publish --tag="breadcrumbs-config"
```

Full reference:

KeyDefaultDescription`view``breadcrumbs::tailwind`Blade view used by ``. Switch to `breadcrumbs::bootstrap5` or any published/custom view name.`home_label``'Home'`Label used by `Trail::home()`.`home_route``'home'`Route name used by `Trail::home()` to generate the home URL.`definition_file``base_path('routes/breadcrumbs.php')`Auto-loaded if the file exists. Set to `null` to disable.`throw_on_missing``env('APP_DEBUG', false)`Throw `MissingBreadcrumbException` when no breadcrumb can be resolved. Defaults to debug mode so production stays silent.`cache_store``env('BREADCRUMBS_CACHE_STORE', null)`Cache store for per-route caching. `null` uses the application default.`cache_tags``['breadcrumbs']`Default cache tags applied to cached breadcrumb entries.`livewire``env('BREADCRUMBS_LIVEWIRE', false)`Enable Livewire `wire:navigate` awareness. Requires `livewire/livewire`.Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [Mike Wall](https://github.com/daikazu)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

41

—

FairBetter than 89% of packages

Maintenance90

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

51d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/00a5aa701f964455918b2e454e7b460fe2ef729639337a059d5bac12e162027e?d=identicon)[daikazu](/maintainers/daikazu)

---

Top Contributors

[![daikazu](https://avatars.githubusercontent.com/u/4039367?v=4)](https://github.com/daikazu "daikazu (3 commits)")

---

Tags

laravelbreadcrumbsdaikazu

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/daikazu-breadcrumbs/health.svg)

```
[![Health](https://phpackages.com/badges/daikazu-breadcrumbs/health.svg)](https://phpackages.com/packages/daikazu-breadcrumbs)
```

###  Alternatives

[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.5k25.9M106](/packages/laravel-cashier)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k12.1M99](/packages/laravel-pulse)[laravel-doctrine/orm

An integration library for Laravel and Doctrine ORM

8425.3M87](/packages/laravel-doctrine-orm)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9682.1M97](/packages/roots-acorn)[glhd/gretel

270305.7k4](/packages/glhd-gretel)[laravel/folio

Page based routing for Laravel.

608453.9k27](/packages/laravel-folio)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
