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

Breadcrumbs Laravel Package

wireui/breadcrumbs

A Laravel Blade breadcrumbs component for WireUI apps that renders accessible navigation trails with icons, links, and active-page states. Simple configuration makes it easy to build breadcrumb paths in your views with minimal markup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and confirm the version. v3.3.0 is the first release in this line that declares Laravel 12 compatibility, so anyone on a Laravel 12 app should pin ^3.3 (or dev-main is no longer needed). Older ^3.2 constraints will fail composer update on a fresh Laravel 12 skeleton because the illuminate/* requirements won't resolve — this is a constraint-only release, no API changes.

composer require wireui/breadcrumbs:^3.3

If you're also using wireui/wireui for the icon/component set, upgrade it in the same composer require call. The breadcrumbs view leans on WireUI's Blade components, and mismatched major versions between the two are the most common source of "component not found" errors after an upgrade.

Where to look first.

  1. config/breadcrumbs.php — publish it only if you need to override the view namespace or the default component alias. Most projects never touch it.
  2. routes/breadcrumbs.php — the package auto-loads this file if present. This is where 95% of your work lives.
  3. vendor/wireui/breadcrumbs/resources/views/ — read the default markup before you fight it. Publish with --tag=breadcrumbs-views when you need to restyle.
  4. The Breadcrumbs facade — for(), render(), exists().

First use case. Register a trail for a route name, then render it once in your layout:

// routes/breadcrumbs.php
use WireUI\Breadcrumbs\Facades\Breadcrumbs;

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

Breadcrumbs::for('users.index', fn ($trail) => $trail
    ->parent('dashboard')
    ->push('Users', route('users.index')));

Breadcrumbs::for('users.show', fn ($trail, $user) => $trail
    ->parent('users.index')
    ->push($user->name, route('users.show', $user)));
{{-- layouts/app.blade.php --}}
<x-breadcrumbs />

Dropping <x-breadcrumbs /> into the layout without arguments makes the package resolve the current route name and render its trail automatically — that's the ergonomic win, and it's why route-name-keyed definitions are the pattern to default to.

Implementation Patterns

Route-name-keyed definitions with parent chaining. This is the canonical pattern and the one to enforce in review. Every breadcrumb is keyed by the route name it belongs to, and each definition declares its own parent. The result is a graph you navigate upward from, which means:

  • Adding a page in the middle of a hierarchy touches exactly one definition.
  • Deep-linking to users.show still produces the full Dashboard > Users > Jane trail.
  • You never pass breadcrumb data down from controllers.

Model binding in closures. Extra closure parameters are injected from the route's bound parameters, in declaration order. Match the param names in your route definition ({user}, {post}) to the closure signature and let the container resolve them — do not type-hint an interface the router can't bind, or you'll get a 500 on a page that otherwise renders fine.

Breadcrumbs::for('posts.comments.show', fn ($trail, $post, $comment) => $trail
    ->parent('posts.show', $post)
    ->push("Comment #{$comment->id}", route('posts.comments.show', [$post, $comment])));

Note that parent() accepts trailing args and forwards them to the parent definition. This is the mechanism that makes nested resources work — pass the parent's models through or the parent's closure will fail on missing parameters.

Render explicitly where auto-resolution is wrong. For layouts shared by routes that don't have trails (health checks, webhooks, auth screens), the auto-render behaviour will throw. Two options:

{{-- only render if a trail exists --}}
@if (Breadcrumbs::exists())
    <x-breadcrumbs />
@endif

{{-- or render a specific trail regardless of current route --}}
<x-breadcrumbs :breadcrumbs="Breadcrumbs::generate('users.index')" />

Prefer the guard in the layout. Conditional-rendering-in-the-component is easier than remembering to add breadcrumbs to every new route.

Multi-tenant and locale-aware labels. Wrap display strings in __() at definition time, not in the view — the trail is generated before the view sees it, and translating in the definition keeps the view dumb:

Breadcrumbs::for('billing.invoices', fn ($trail) => $trail
    ->parent('settings')
    ->push(__('nav.invoices'), route('billing.invoices')));

For tenants, resolve the model inside the closure rather than capturing it:

Breadcrumbs::for('tenant.dashboard', fn ($trail) => $trail
    ->parent('home')
    ->push(tenant()->name, route('tenant.dashboard')));

Integration tips.

  • Inertia/Livewire SPA mode is not automatic. Because breadcrumbs are rendered server-side from the current route, a client-side navigation that doesn't trigger a full page load won't refresh them. In Inertia, put the trail on the shared-prop bag (HandleInertiaRequests::share()) and render it from the frontend, or force a partial reload of the layout component. In Livewire with wire:navigate, render the component outside the persisted layout region or it will go stale.
  • SEO. The default markup is a <nav> + <ol>; add JSON-LD BreadcrumbList yourself in the layout using Breadcrumbs::generate() and iterating the trail — the package doesn't emit structured data.
  • Testing. Breadcrumbs::exists('users.show') is the cheap assertion; for output assertions, use assertSeeInOrder(['Dashboard', 'Users']) against the response rather than matching exact markup.

Gotchas and Tips

The Laravel 12 release is a no-op at the API level — but verify your full dependency graph. v3.3.0 only widens the framework constraint. If composer update still fails after bumping, the conflict is almost always a transitive wireui/wireui or illuminate/support pin, not this package. Run composer why-not wireui/breadcrumbs 3.3.0 to find the actual blocker.

Route-name mismatches fail loudly at render time, not boot time. A typo in Breadcrumbs::for('users.show') vs ->name('user.show') produces an exception when the page is requested. On a public-facing route that's a 500. Do a one-off coverage pass:

// in a test or a tinker session
Route::getRoutes()->getRoutesByName();
array_diff(array_keys(Route::getRoutes()->getRoutesByName()), array_keys(Breadcrumbs::get()));

You don't need 100% coverage, but you do need the layout guard described above so an uncovered route degrades to no breadcrumbs instead of an error page.

Don't cache closures. Breadcrumb definitions are closures registered via the service provider. They cannot be serialized into config:cache or a route cache, and any attempt to memoize them will blow up. Definitions re-register every request; that's cheap, but don't "optimize" it.

routes/breadcrumbs.php is not route-cached. Consequence: it's loaded even in route:cache-deployed apps, so filesystem reads on every request. Keep it lean — no DB queries at definition time. Resolve models lazily inside closures, never at the top of the file.

The current page should be a <span>, not a link. The default view handles this, but the moment you publish and customize the views this is the first thing that regresses. Check it with a keyboard/screen-reader pass — a self-referencing link in a breadcrumb trail is a real accessibility defect, and the aria-current="page" attribute needs to survive your restyle.

Tailwind purge will eat your classes. If you publish the views and then restyle with dynamically constructed class strings, Tailwind's content scanner won't see them. Add the published view path to content in tailwind.config.js and keep class names as complete literals.

Dark mode is inherited, not configured. The default markup ships Tailwind dark: variants driven by your app's dark-mode strategy. If your app uses a class-based strategy but doesn't put dark on <html>, the breadcrumbs will look broken while the rest of the UI looks fine — check the root element before debugging the package.

Extension points, in order of preference. (1) Publish and edit the views for styling; (2) subclass or alias the Blade component in config/breadcrumbs.php if you need behavioural changes; (3) publish the config to change the default view/component names. Avoid forking the package — the API surface here is small and stable, so the published-view route covers nearly everything.

Upgrade checklist for v3.3.0. Bump the constraint, run the suite, and specifically exercise any route using model binding in a breadcrumb closure — those are the definitions most sensitive to framework-level changes in parameter resolution, and they're the ones that fail silently by rendering a shortened trail rather than throwing.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core