log1x/crumb
Crumb is a lightweight breadcrumbs package for Laravel. Define navigation trails with a simple fluent API and render them through a Blade component, with straightforward options for customizing markup, separators, and styling to match any theme.
Crumb is a small, single-purpose package: you push a list of [title, url] pairs, and it renders them as breadcrumb markup for you. There is no database, no admin UI, and no route magic — the trail is whatever you tell it to be during the request. That makes the first-use path short.
Install via Composer in the Laravel app (or in an Acorn/Sage theme if that is your host — the package supports both and v1.1.5 exists purely to keep it working under Acorn 4.x):
composer require log1x/crumb
Laravel package auto-discovery registers the service provider and Crumb facade; there is nothing to add to config/app.php.
Resolve the facade first. Before building anything, drop a single call into a controller or a throwaway route and confirm it resolves:
use Log1x\Crumb Facades\Crumb;
Crumb::add('Home', url('/'));
If that throws a binding error you are on a broken install, and nothing downstream will work.
Render it once. Add the Blade output to a layout and check it visually:
{!! Crumb::render() !!}
Getting default markup on screen is the "hello world" test. Only after that is worth customising.
Publish the package assets if/when you need to change the markup or default behavior. php artisan vendor:publish and look for the log1x/crumb provider tag. The published Blade view is where 90% of customisation should happen.
If you are only ever going to render a three-level trail, the above is genuinely the whole integration. Everything below is about keeping it tidy as the app grows.
Centralise trail construction, don't scatter Crumb::add() in views. The package doesn't care where you call it from, and Blade is the worst place — the trail becomes impossible to test or reuse. Two patterns work well:
Breadcrumbs service that builds a trail from the route name plus route parameters, then is injected into the controller and flushed to the facade. This is the pattern that scales when you have 40 resource routes.Driving crumbs from route data. The most maintainable approach for a conventional Laravel app is to key off route names and parameters rather than hardcoded strings:
Crumb::add('Home', route('home'));
if ($post = request()->route('post')) {
Crumb::add('Blog', route('blog.index'));
Crumb::add($post->title, route('blog.show', $post));
}
Using route() instead of url() means the trail survives URL restructuring and respects the locale/domain your app is served from. Build the array once in a service and reuse it in tests.
Model-driven trails. For a CMS-style app, put a breadcrumbs() method (or a small interface) on the model that returns its ancestors — categories, parents, whatever the hierarchy is — then map that to Crumb::add() calls. This keeps taxonomy logic on the model and leaves the package as a dumb renderer, which is exactly what you want from a 43-star utility.
Custom markup via a Blade view. Rather than fighting the default output, publish/replace the rendering view with the exact <nav><ol> markup your CSS expects. The package is designed for this — a view-based renderer is the right seam for design changes and means your styling lives with your other Blade partials.
Consistent placement in the layout. Render breadcrumbs inside a named section or partial in your base layout so every page gets a trail without a template change. Combine that with a @hasSection guard or a check on Crumb::all() being non-empty so pages without a defined trail don't render empty markup.
Crumb::render() returns HTML, so it belongs in {!! !!}. If you are rendering titles from user-supplied content (CMS post titles), confirm your override view escapes with {{ }}. Never {!! $crumb['title'] !!}.boot().AppServiceProvider::boot(). No route is resolved yet, so request()->route() is null. Use a view composer or middleware, both of which run after routing.<ol> in a layout is a common and slightly embarrassing first bug; either skip the partial when there are zero crumbs or have your override view return nothing.url('blog') and route('blog.index') can produce different strings than your nav links, causing CSS aria-current or active-state checks to fail silently. Use route() consistently.Crumb::all() in tinker or dd()), (3) is the correct view being used after publish, (4) is the layout passing the right data. Skipping to CSS is nearly always wasted time on what is a data problem.^1.1 is safe, but a surprise composer update flipping a transitive Acorn constraint is the standard way this package "stops working" without any code change.How can I help you explore Laravel packages today?