chris-ware/nova-breadcrumbs
chris-ware/nova-breadcrumbs adds configurable breadcrumb trails to Laravel Nova, making it easier to navigate resource indexes, detail pages, and custom screens. Simple to install and customize for clearer orientation in Nova admin panels.
Day-to-day, this package is three touchpoints: the tool registration in NovaServiceProvider, the config that defines trails, and the resource classes that surface them. Nothing else in your app should change.
Before anything else, confirm your Nova major. The last release is July 2021 and targets the Nova 3.x / Laravel 8 era. On Nova 4.1+ breadcrumbs ship natively — stop and check whether your need is already met. If you are on Nova 3.x, proceed knowing you are adopting frozen code you now own.
Install and register:
composer require chris-ware/nova-breadcrumbs
php artisan vendor:publish --provider="ChrisWare\NovaBreadcrumbs\ToolServiceProvider"
php artisan nova:asset chris-ware/nova-breadcrumbs
Pin an exact version — there are no future tags, so a floating constraint only adds risk.
// app/Providers/NovaServiceProvider.php
public function tools()
{
return [
new \ChrisWare\NovaBreadcrumbs\NovaBreadcrumbs(),
];
}
Where to look first, in order:
README.md — the package is a thin Nova tool; nearly all usage documentation lives here.src/ — the Tool class registered with Nova, the service provider that merges config and views, and the class or trait exposing the breadcrumb API to resources.config/ — where trails are actually defined day-to-day.resources/views/ and resources/js/ — the rendered markup and asset hook, so you know what you can override.First use case: wire one resource detail page to a two-level trail — Home › Users › Jane Doe. Confirm it renders, confirm nova:asset publishes cleanly, then generalize. Resist building the full hierarchy config before you have seen one trail render; asset and registration failures surface on the first resource and are cheap to fix there.
Verify namespaces against composer.json autoload before copy-pasting the tool class name — the vendor namespace in docs and the autoload map occasionally drift.
Treat the config as a map from resource key to an ordered list of segments. The 80% case is flat: each segment is a label plus a route target (Nova::path(), a named route, or a resource index URI). This gets you "back to the index" on detail and edit screens with almost no ceremony.
'users' => [
['label' => 'Users', 'url' => '/resources/users'],
['label' => fn ($model) => $model->name],
],
Prefer parent-chain traversal for nested data. For Project → Board → Card, do not hardcode full trails per resource. Declare one parent per resource, walk up the chain at render time, and assemble the trail dynamically. Adding an intermediate level then becomes a one-line edit instead of a sweep through every config entry — this is the pattern that keeps the package usable past ~20 resources.
Use closures for dynamic labels, not static strings. The index trail wants Users; the detail trail wants Jane Doe. Labels should resolve against the current model instance (fn ($model) => $model->name). Confirm the package passes the model into your resolver; if it does not, fall back to Route::current()->parameter('resource') or resolve from the request at render time.
Put the breadcrumb method on a BaseResource and inherit. Implement it once on a shared base resource that feature resources extend. Your callsites then live in your codebase — not scattered through a vendor config file — which is also the fastest thing to excise if you later migrate to Nova's native breadcrumbs.
Register defensively and gate behind a config flag. Nova tools are global chrome: a throwing tool returns a 500 on every admin page, not one screen. From day one: wrap rendering in try/catch that degrades to no breadcrumbs, gate the tool behind a config flag, and roll out to one resource group before enabling everywhere.
Spend breadcrumbs where they pay. They earn their keep on resources reachable from several entry points (search, related-resource cards, lenses) and on custom tool pages several clicks deep. On top-level index pages the sidebar already answers "where am I" — there they are clutter. Audit your admin for disorientation before wiring trails everywhere.
Test the integration cheaply. Output is server-side Blade over an in-memory array. A Dusk or HTTP test asserting breadcrumb markup on one detail and one edit page catches Nova upgrades that break the view before users do — and it is the only regression net you will have, since upstream will not ship fixes.
Day-to-day workflow: new resource → add a config entry or parent declaration → verify on detail and edit → move on. Keep that loop under five minutes per resource or your config has drifted toward the wrong shape.
Archived means you are the maintainer. No CVE path, no issue triage, no version aligned to a future Nova upgrade. Name an owner, add a standing line item against every Nova major upgrade (~18–24 month cadence) to re-verify component APIs and asset builds, and write that in your README — not in someone's head. If adoption is mandated, fork to an internal repo (Satis / private Packagist) rather than pulling from Packagist directly. A clean reimplementation is roughly 1–2 engineer-days, so the fork has a low bar to beat.
Nova major mismatch is the dominant failure mode. A 2021 release places this in Nova 3.x. Nova 4/5 reworked tool asset registration, front-end build tooling, and component APIs. If the repo does not ship compiled dist/ assets, you will be rebuilding against a 2021 Node/Laravel Mix toolchain — that is where estimates slip. Do a half-day staging spike on your exact production Nova version before committing.
Closures in config break config:cache. If trail definitions use closures for dynamic labels or URLs, php artisan config:cache will fail or silently strip them. Either keep breadcrumb config out of the cached config path (a dedicated PHP file loaded by the service provider) or resolve labels at render time from the model/route rather than from the config array. Check this in CI — a production deploy that runs config:cache is where this bites.
Global failure equals total admin outage. A Blade view referencing a removed Nova API throws on every admin page. Design against it from the start: per-resource registration where possible, defensive rendering, a feature flag, and never ship without a staging pass on the exact Nova version in production.
Publish tags and view overrides. vendor:publish --force overwrites customised views without warning. Record the tag you published under and keep overrides as a small, documented diff. When debugging an empty or broken strip, check in this order: php artisan view:clear, php artisan config:clear, then the compiled asset manifest. Publish mismatches are the usual culprit and are cosmetic, not blocking.
Config cardinality scales linearly. Per-resource trails mean 60 resources = 60 hand-maintained entries. Prefer request-derived or convention-based trails (parent-chain resolution) if you extend the package or reimplement it, and price that maintenance into the estimate.
Route model binding mismatches. Detail-page trails depend on the current route's bound model. Lens filters, custom tool pages, and non-resource routes will not have one — null-check every resolver and fall back to a static trail rather than throwing.
Localization and accessibility are 2021-era. No shipped i18n, and likely no nav landmark or aria-current semantics. If your compliance bar needs either, budget it yourself — and treat that as another argument for reimplementing rather than pinning.
How can I help you explore Laravel packages today?