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

Sf Breadcrumbs Laravel Package

libriciel/sf-breadcrumbs

libriciel/sf-breadcrumbs is a lightweight breadcrumb package for PHP applications, offering a simple API to define, manage, and render navigational trails with minimal configuration. It helps keep page hierarchy clear and site navigation consistent across views.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first: this is a Symfony bundle, not a Laravel package. There is no composer require that will give you a working breadcrumb trail in a Laravel app. The bundle registers itself through Kernel::registerBundles(), hooks Symfony's DI container and RequestStack, and ships a Twig extension — none of which boot under Laravel's service provider kernel. composer require libriciel/sf-breadcrumbs may resolve (Laravel pulls individual symfony/* components), but that is a false positive. Installation is not integration.

Where to look first — the source, not the install. Clone the repo and read these three things in order, because they define the whole contract:

  1. The bundle manifest and DI config (libriciel/sf-breadcrumbs's *Bundle.php, Resources/config/services.*). This tells you the service identifier that owns trail assembly and how it is wired. That single service is the thing you are actually going to reimplement.
  2. The trail/item classes — the value object with {label, url} and the collection/resolver that returns an ordered list. This is the portable idea.
  3. The Twig extension — it shows how the final crumb is downgraded from a link to plain text and how separators/labels are overridden. This is the rendering contract you'll mirror in Blade.

Skip the bundle plumbing (CompilerPass, Extension, Configuration). It exists to hook Symfony's lifecycle and has no meaning in Laravel.

First use case. Derive a trail from a named route, render it once in a layout, and validate with a screen reader. Concretely, the smallest useful thing:

// app/Support/Breadcrumbs.php — a singleton you bind in a ServiceProvider
Breadcrumbs::for('posts.index', fn ($t) => $t->push('Posts', route('posts.index')));
Breadcrumbs::for('posts.show', fn ($t) => $t->parent('posts.index')->push($post->title));
{{-- resources/views/components/breadcrumbs.blade.php --}}
<nav aria-label="Breadcrumb">
  <ol>
    @foreach ($trail as $crumb)
      <li>
        @if ($crumb->isCurrent())
          <span aria-current="page">{{ $crumb->label }}</span>
        @else
          <a href="{{ $crumb->url }}">{{ $crumb->label }}</a>
        @endif
      </li>
    @endforeach
  </ol>
</nav>

You reach this in an afternoon by reading the package, not by installing it. Treat the repo as a reference implementation and start here.

Implementation Patterns

The three-layer split the package implies, ported to Laravel. The bundle cleanly separates definition (where crumbs are declared), resolution (assembling the trail for the current route), and rendering (Twig). Keep that separation; it is the one design decision worth copying.

  • Definitions live in a single routes/breadcrumbs.php file loaded by a BreadcrumbServiceProvider::boot(). Per-route closures, registered like route model bindings. This mirrors the package's config-driven trail definitions.
  • Resolution is a Breadcrumbs singleton with a ->for($route) / ->render() API. Resolve lazily — compute the trail only when the component asks for it, exactly as the bundle's service does behind its Twig function.
  • Rendering is a Blade component, not a directive. Components are the Blade equivalent of a Twig function plus template.

Parent chaining for nested routes. The package's auto-detection of the current page and trailing-item handling is really "walk up the route tree and mark the last node as current." Do this with explicit parents rather than string surgery on URL segments:

Breadcrumbs::for('admin.users.edit', fn ($t, $user) => $t
    ->parent('admin.dashboard')
    ->parent('admin.users.index')
    ->push($user->name, route('admin.users.show', $user))
    ->push('Edit'));

The terminal push() with no URL is the "current page" convention — same as the bundle's trailing non-linked item. Explicit parents beat URL-parsing because Laravel named routes and route-model binding already encode the hierarchy; you get dynamic parameters ($user) for free, which a URL-segment parser cannot.

Register at the highest layout level. Call <x-breadcrumbs :trail="$trail" /> once in your base layout and let @yield/slots handle page placement. If you wire it per-view you will forget on the next screen — this is the integration tip that saves the most rework, and it matches how the bundle expects to be dropped into a Twig base template.

Fallback for uncovered routes. The package has fallback resolution for pages not in config. Your equivalent: a resolver that, when no definition exists for the current route, synthesizes a trail from the route name (admin.users.show → Admin / Users / Show) or returns an empty trail and renders nothing. Prefer empty trail in development (visible, obvious) and a synthesized fallback in production.

JSON-LD as a separate opt-in component. Don't couple structured data to layout. Emit BreadcrumbList from its own component so it can be tested and validated independently of markup or CSS changes:

<x-breadcrumbs.json-ld :trail="$trail" />

Testing pattern. Test trail assembly for three cases: static route, parameterized/route-model-bound route, and fallback/404. The bundle's edge cases (missing parent, dynamic segments, current-page-as-text) are exactly your test list — read them out of the source and assert on them.

Gotchas and Tips

The AGPL-3.0 license is the real blocker, not the framework mismatch. §13's network-use clause means if you modify the code and serve the result over a network — which is what a SaaS does — you may be obligated to offer the modified source to users. For a proprietary or customer-facing product this needs counsel before an engineering spike. Even in a fork-and-port scenario, copying source verbatim carries the obligation; reading it for design and reimplementing clean-room is the only path that avoids it. Decide this first; it gates everything else.

Don't install it "just to see." A composer require on a Laravel app will resolve dependencies and look successful, then fail at boot with no obvious error — Symfony bundle registration has no Laravel equivalent. Worse, a resolved AGPL dependency in composer.lock is a compliance artifact someone will find later. Read the source instead.

Zero adoption means you own every bug. 0 stars, 0 dependents. There is no issue-tracker mass, no Stack Overflow corpus, no community patch flow. Upstream will not patch security issues for you or deprecate gracefully with your version constraints in mind. Do not pin your hopes on upstream responsiveness for a breadcrumb.

Version constraints before anything. If you do pursue it in any capacity, check the package's composer.json for PHP and Symfony require constraints and whether it transitively pulls sibling Libriciel packages. Sibling bundles multiply the audit surface — a breadcrumb package that drags in three other bundles is not "dependency-light" in practice, whatever the description says.

Extension point worth knowing. The bundle's custom separators, labels, and URL overrides are all config, resolved at render time. If you port this, make the label/URL override a first-class method on your item object rather than a config array — Laravel developers expect fluent builders, and it composes better with route-model binding (->push($post->title) beats a config key that maps to a route name).

Debugging the trail. When a breadcrumb renders wrong, the culprit is almost always the parent chain, not the renderer. Log the resolved trail (dump(Breadcrumbs::for(request()->route()->getName()))) rather than inspecting HTML — the package resolves trail and renders separately, and so should you; knowing which layer broke is most of the debugging.

The current crumb must not be a link. This is the accessibility detail that separates a real breadcrumb from a list of anchors. aria-current="page" on a <span>, never an <a> to the page you're already on. The bundle does this correctly; it's the one behavior to verify first in your ported component, and it's easy to regress during a layout 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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels