Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Breadcrumbs Laravel Package

yajra/laravel-breadcrumbs

A lightweight breadcrumb package for Laravel that helps you define and render breadcrumb trails in Blade views. It supports route-based breadcrumb definitions and simple customization, making it easy to add structured navigation to your application without complex configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require yajra/laravel-breadcrumbs

Before writing a single line of code, run:

composer show yajra/laravel-breadcrumbs

Read the require block. The illuminate/support constraint is the whole compatibility story for a package with a "Laravel 5.4+" claim — if it doesn't cover your major, stop and treat the source as a reference implementation instead. On Laravel 5.5+ the service provider is auto-discovered; there is no config/app.php edit unless you're on 5.4.

Where to look first, in order:

  1. vendor/yajra/laravel-breadcrumbs/views/ — the shipped templates (bootstrap2/3/4 lineage). This is your real API surface for markup.
  2. config/breadcrumbs.php after php artisan vendor:publish — two keys matter: view (which template renders) and files (where definitions are loaded from, default routes/breadcrumbs.php).
  3. src/BreadcrumbsServiceProvider.php — confirms how the definitions file is required and whether a Breadcrumbs facade alias is registered (relevant if you already have one).

First use case — a flat two-level trail. Create routes/breadcrumbs.php:

use Yajra\Breadcrumbs\Facades\Breadcrumbs;

Breadcrumbs::for('home', fn ($trail) => $trail->push('Home', route('home')));

Breadcrumbs::for('posts.show', function ($trail, $post) {
    $trail->parent('home');
    $trail->push($post->title, route('posts.show', $post));
});

Render it from one layout partial, not from controllers:

{{-- resources/views/partials/breadcrumbs.blade.php --}}
{{ Breadcrumbs::renderIfExists() }}

renderIfExists() resolves the current route name automatically and renders nothing when no definition matches — a safe default for a layout that gets included everywhere. Breadcrumbs::render('posts.show', $post) is the explicit form for show pages where the layout doesn't know the model.

Then immediately wrap it:

{{-- resources/views/components/breadcrumbs.blade.php --}}
<nav aria-label="Breadcrumb">{{ Breadcrumbs::renderIfExists() }}</nav>

Everything above the package — layouts, tests, presenters — should reference <x-breadcrumbs />, never the facade. That single indirection is the difference between "removing the package is a file edit" and "removing the package is a refactor."

Implementation Patterns

Group definitions by section, mirroring your route files. One routes/breadcrumbs.php gets unwieldy fast; keep admin, catalogue, and account blocks separated by comments and review them alongside route changes. A missing definition is a silent empty nav, so co-locating the diff is your cheapest regression control.

Chain with parent(), not by duplicating pushes. parent() inserts the parent's trail at the point of the call, so call it before push():

Breadcrumbs::for('catalogue.products.show', function ($trail, $category, $product) {
    $trail->parent('catalogue.categories.show', $category); // inserts Home > Category
    $trail->push($product->name, route('catalogue.products.show', [$category, $product]));
});

Callback arguments map positionally to route parameters. Declare defaults for optional parameters in the closure signature so definitions don't blow up when a segment is absent.

Use exists() to guard optional segments. Where a trail is genuinely conditional (draft previews, unlisted pages), check Breadcrumbs::exists('route.name') and branch rather than relying on the exception path.

Pass a data payload for anything the template needs beyond title/url. The third argument to push() lands on $breadcrumb->data, which is the supported way to add icons, counters, or "current" markers without stuffing HTML into the label:

$trail->push('Users', route('admin.users.index'), ['count' => $userCount]);

Custom templates: publish the views to resources/views/vendor/breadcrumbs/ and edit, or point config('breadcrumbs.view') at your own Blade file (e.g. partials.breadcrumbs-nav). Prefer the config pointer — it keeps the trail markup in your app tree where it's greppable and reviewed, and survives composer update.

JSON-LD is yours to add. The package emits nav markup only. Generate the trail once and map it:

$trail = Breadcrumbs::generate(); // array of objects with ->title / ->url
// map to schema.org BreadcrumbList, emit in a <script type="application/ld+json">

If SEO rich results are the actual requirement, budget this as separate work — the package saves you nothing here.

Localise in the definition, not the view:

$trail->push(__('nav.home'), route('home'));

Definitions are plain PHP evaluated at render time, so __() composes normally, and locale switches per request.

Testing. One feature test per major route group, asserting the rendered trail end-to-end through the layout:

$this->get(route('catalogue.products.show', [$category, $product]))
     ->assertSee('Home')
     ->assertSee($category->name)
     ->assertSee($product->name);

Add DB::enableQueryLog() assertions for any definition that resolves a model — see the N+1 note below.

Migration pattern. Replacing an existing breadcrumb implementation? Only routes/breadcrumbs.php and the template change. Keep the <nav><ol> contract identical so the visual diff is zero, and migrate section by section (settings → admin → catalogue) rather than site-wide, so a missing definition degrades one page instead of every page.

Gotchas and Tips

  • Silent degradation is the default failure mode. An undefined route renders an empty <nav> — the site looks fine and navigation is gone. Decide per surface: render() (explicit, throws on a bad name) in section layouts where you want dev-time noise, renderIfExists() in the global layout where a 500 is worse than a missing crumb.
  • Facade alias collisions. This package descends from davejamesmiller/laravel-breadcrumbs, and diglactic/laravel-breadcrumbs is the maintained fork of the same lineage. Two of them bound at once shadow each other's facade and produce confusing view behaviour. Enforce a single breadcrumb owner in code review; if you're on Laravel 10/11, that owner should probably not be this repo.
  • Escaping. The shipped templates use {{ }} on titles — keep it that way in any custom view. Labels routinely interpolate model names and user input, so {!! $breadcrumb->title !!} is a stored-XSS vector with a friendly face. If a designer needs an icon in a crumb, add it in the template as markup around the escaped title, not inside the title.
  • Route/parameter drift. Renaming catalogue.products.show or its {product} parameter breaks the definition silently (empty trail) or loudly (Missing required parameter). Add "update routes/breadcrumbs.php" to your route-change checklist; the feature tests from the previous section are what actually catch it.
  • N+1 hiding in a "template helper." Callbacks that lazy-load a parent category per request add queries nobody profiles. Pass route-bound or eager-loaded models into the callback, and assert query counts in the breadcrumb test.
  • route() vs URL::to(). Use route() with the model/params — it encodes parameters, respects domain/subdirectory config, and keeps signed or localised URL logic in one place.
  • No caching, no queue, no I/O. Cost is a handful of array operations per request; don't add your own cache layer around it without measuring. If you do cache rendered HTML, key on locale and user role — the trail often leaks names.
  • Pin the version and commit the lock file. With zero dependents and no visible release cadence, an unpinned composer update is the main way this package breaks your build. Treat it like a vendored dependency with an audit date.
  • The Blade directive (if your version registers one) is sugar over the facade. Prefer Breadcrumbs::renderIfExists() inside <x-breadcrumbs /> — one call site to grep, one place to swap to an in-house implementation, one file to edit if you ever vendor the source into app/Support/.
  • Vendoring rehearsal, two hours. Copy src/ into app/Support/Breadcrumbs, keep the same facade name, run the tests. The equivalent implementation is roughly 30–50 lines; proving that to yourself converts the supply-chain risk from an open question into a known, cheap exit.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console