bocanhcam/nova-breadcrumb
Adds customizable breadcrumb navigation to Laravel Nova, helping users understand where they are and move quickly between resources, pages, and dashboard views. A lightweight package for improving Nova admin navigation.
Install and register
composer require bocanhcam/nova-breadcrumb:^1.0
The package is a Nova extension, so it needs a Nova app (v4 or v5 — check the composer.json require block before upgrading Nova, since the v1.0.7 → v1.0.9 range may not have been re-tested against the newest Nova major).
If the service provider is not auto-discovered, add it to config/app.php, then publish any assets/config:
php artisan vendor:publish --provider="Bocanhcam\NovaBreadcrumb\ToolServiceProvider"
Where to look first, in order
README.md — the only reliable source of usage here, because the release notes for v1.0.9 are a bare changelog link with no descriptive text.config/nova-breadcrumb.php (or equivalent published config) — this is where separator, home label, and trail-definition hooks normally live.src/ — specifically the class that builds the trail. This is the file you will end up extending.composer.json — confirm the illuminate/* and laravel/nova constraints before assuming compatibility.First use case
The highest-value entry point is a nested detail page. Take a Project → Task → Comment chain: open a Comment in Nova and, without configuration, the user sees only "Comments". With a breadcrumb trail they see Home / Projects / Acme Redesign / Tasks / Fix login / Comments, which is the difference between a usable and an unusable admin for deep relations.
Start by getting a single hardcoded trail rendering on one resource detail page. Once that renders, generalise it. Do not start by wiring every resource — you will fight the config surface before you know what it actually does.
Per-resource trail declaration
The common shape is a method or property on the Nova resource that returns an ordered array of segments. Keep each segment to { label, url? } and let the last item be un-linked (the current page). Centralise the builder so resources don't each invent their own format.
public function breadcrumbs(NovaRequest $request): array
{
return [
Breadcrumb::make('Projects', '/resources/projects'),
Breadcrumb::make($this->project->name, "/resources/projects/{$this->project->id}"),
Breadcrumb::make('Tasks', "/resources/tasks?viaResource=projects&viaResourceId={$this->project->id}"),
Breadcrumb::make($this->task->title),
];
}
Relationship-driven generation
Rather than hand-writing trails, walk the parent chain. If your models expose a parent() / parentResource() contract, a single resolver can produce trails for every nested resource and stay correct as the hierarchy grows. This is the pattern worth investing in; the hand-written version rots quickly.
Integration with Nova's own navigation
Render the trail in a place that survives Nova's SPA navigation — a custom Nova::script/Nova::style hook or a resource tool, rather than raw Blade injected into a layout Nova re-renders. If the package ships a Tool, register it in NovaServiceProvider::tools(); tools are the most upgrade-stable integration point.
Multi-tenancy
Under Nova's Laravel\Nova\Fields\BelongsTo tenancy, the trail must respect tenant scope. Filter segments by Nova::getTenantResolverForUser() or you will leak the tenant context of another workspace into the trail URL.
Custom rendering
Override the view rather than editing vendor files:
php artisan vendor:publish --tag=nova-breadcrumb-views
Then restyle into your existing design system. Pin the published view next to your own components so a future release that changes the view variables surfaces as a diff, not a silent break.
Testing
Assert on the generated trail array in a unit/feature test rather than on rendered HTML — array assertions are cheap and survive view changes. Add one Nova Dusk or HTTP test that hits a nested detail page to catch the case where the trail renders but the URLs 404.
The changelog is a link, not a changelog. v1.0.9 (and v1.0.8) shipped with release notes containing only a compare/v1.0.7...v1.0.9 URL. There is no way to know from the release whether anything broke. Pin the version exactly ("bocanhcam/nova-breadcrumb": "1.0.9") and read the actual diff — git diff v1.0.7 v1.0.9 -- src/ — before letting it float. An unreviewed two-version jump on a package that touches every detail page is a poor trade.
N+1 in the trail. Relationship-driven trails are the classic place to accidentally lazy-load four levels of parents per request. Eager-load the chain in indexQuery/detailQuery or memoize the resolved trail per request. This is invisible locally and painful in production.
Config cache. If you add trail definitions to a published config file, remember php artisan config:cache in deploy. A trail that works in dev and disappears in production is almost always a cached config or a cached Nova component manifest.
URL drift on Nova upgrades. Nova changes URL query conventions (viaResource, viaResourceId, viaRelationship) between majors. If the package builds trail links for you, re-test nested navigation after every Nova upgrade, not just after every package upgrade.
Segment escaping. Resource labels are user data. If the trail is rendered with {!! !!} anywhere in the published view, confirm the labels are escaped — an admin user naming a record <script> should not be able to XSS the panel.
Extension points worth knowing. The published config (defaults/separator/labels), the published views, and the trail-builder class are the three hooks available. Prefer the builder class over config for anything conditional — config is static and cannot branch on the current resource.
Where to spend debugging time. If the trail is empty, check in this order: provider registered → config published and cached → trail method actually on the resource being resolved → Nova tool/nav hook registered. Empty trail is nearly always step 2 or step 4, rarely a bug in the package itself.
Overall. This is a small, focused utility that solves one problem well: orientation in deep Nova hierarchies. Adopt it, but treat each release as unaudited until you have read the diff — the packaging discipline around release notes does not currently support blind upgrades.
How can I help you explore Laravel packages today?