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

Crumb Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

Implementation Patterns

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:

  • View composer in a service provider, keyed to the layouts that render breadcrumbs. The composer inspects the current route/model and builds the trail. One place to change, easy to feature-test.
  • Middleware or a dedicated 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.

Gotchas and Tips

  • Order is insertion order. The last crumb is conventionally the current page and usually shouldn't be a link. If your markup links the current page, strip the URL for the final item in your rendering view rather than trying to special-case it in the service.
  • Escaping. 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'] !!}.
  • Per-request state. The facade is a singleton, so crumbs accumulate for the lifetime of the container. Under Octane or long-running workers this leaks across requests unless you reset it — rebuild the trail in middleware or a view composer on every request rather than in a service provider's boot().
  • Don't build trails in AppServiceProvider::boot(). No route is resolved yet, so request()->route() is null. Use a view composer or middleware, both of which run after routing.
  • Empty trails. Guard the render call. An empty <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.
  • Trailing/leading slash drift. 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.
  • Debugging order when it renders wrong: (1) is the facade resolving, (2) is the trail being built at all (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.
  • Extension points are the published render view and the facade/service binding. Subclassing package internals is not worth it — the codebase is small enough that a forked view covers virtually every need, and it survives patch releases.
  • Pin with a caret and commit the lock file. Releases here are dependency chores (v1.1.5 is only an Acorn 4.x compatibility fix), so ^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.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings