graham-campbell/navigation
Graham Campbell's Navigation package is a lightweight Laravel tool for building and managing site menus. Define navigation structures with a simple, expressive API, handle nested items and active states, and render menus cleanly in Blade templates. It keeps menu configuration readable and reusable a...
This is a Laravel 5-era package for legacy Laravel 5 maintenance — a tiny fluent navbar builder. Do not start a new Laravel 6+ project with it.
Install:
composer require graham-campbell/navigation
Register the provider and facade manually on Laravel 5.0–5.4 in config/app.php:
'providers' => [
// ...
GrahamCampbell\Navigation\NavigationServiceProvider::class,
],
'aliases' => [
// ...
'Navigation' => GrahamCampbell\Navigation\Facades\Navigation::class,
],
Laravel 5.5+ may auto-discover the provider, but verify the Navigation alias actually resolves before relying on it.
Publish the config (and any views):
php artisan vendor:publish --provider="GrahamCampbell\Navigation\NavigationServiceProvider"
Define a first menu and render it:
Navigation::add('Home', '/');
Navigation::add('About', '/about');
Navigation::add('Contact', '/contact');
{!! Navigation::render() !!}
Where to look first:
add/route/URL/child syntax — the API is small, so the source is often faster than docs.src/Navigation.php) to list available methods (add, nested/child helpers, clear/reset, render).config/navigation.php for view and rendering options.resources/views; copy it into resources/views/partials/ when you need custom markup.First real use case: a top navbar with Home/About/Contact, active-item highlighting, and Bootstrap-compatible markup on an existing Laravel 5 app.
Centralize definitions in a view composer, not a controller. Keep menu state out of controllers and let auth-dependent items resolve after the session is available:
View::composer('layouts.app', function ($view) {
Navigation::add('Home', route('home'));
Navigation::add('About', route('about'));
if (Auth::check()) {
Navigation::add('Dashboard', route('dashboard'));
}
});
Avoid building menus in AppServiceProvider::boot() unless you are certain auth state is ready at that point.
Feed it route names or route() output. Active detection is only as good as the URL you give it. Pass route('users.index') rather than hand-written paths so it survives route changes.
Nesting and dropdowns. Use the package’s child/array API, then confirm the default renderer actually outputs nested markup. If it flattens children, override the view:
// config/navigation.php
'view' => 'partials.navigation',
Create resources/views/partials/navigation.blade.php, iterate the collection the renderer exposes, and dd() the item objects once to learn their shape before writing the partial.
Multiple menus in one request. If the facade is a singleton builder, items can leak between menus. Look for a clear()/reset()/flush() and call it before building each menu, or wrap menu creation in a small service that rebuilds state deterministically.
Wrap it behind an internal interface. Even day-to-day, keep facade calls in one menu service/provider and render through a single Blade partial. This makes the eventual replacement a one-file change and keeps package-specific classes out of presentation code.
Cache static structure, not active state. Active state depends on the current request; cache only the assembled tree if building it is expensive. Watch for per-item DB or authorization calls in dynamic menus (N+1 queries).
Testing. Assert rendered HTML contains the expected links and active class, or swap/mock the facade if you only care that calls happen. Add contract tests for markup, URLs, nesting, and active states — these are exactly the things that break on framework upgrades.
Exit path. Pin the exact version in the lock file, vendor or fork it, and schedule replacement before the next Laravel/PHP upgrade. A handful of Blade + config code is a realistic internal replacement.
composer why-not against your pinned Laravel/PHP before promising an install, and be ready for constraint conflicts on any upgrade./about/team may not mark /about active. Override the view or set active state explicitly; add tests for wildcard and parameterized routes where false positives are common.php artisan config:clear, view:clear) — otherwise you may debug phantom behavior.{!! !!} is required for markup output, but confirm the package escapes labels and URLs. Unescaped labels/URLs are an XSS risk if any menu content is user- or CMS-driven.How can I help you explore Laravel packages today?