spatie/laravel-navigation
Define a navigation tree for Laravel apps and reuse it to build menus, breadcrumbs, and other nav UI. Manage sections, nested items, and active state in PHP, register navigation via service providers/container events, and render however you like.
Installation:
composer require spatie/laravel-navigation
Publish the config (optional):
php artisan vendor:publish --provider="Spatie\Navigation\NavigationServiceProvider"
First Use Case: Define a navigation tree in a service provider or controller:
use Spatie\Navigation\Navigation;
use Spatie\Navigation\Section;
Navigation::make('main', function () {
return [
Section::make('Home', route('home'))
->activeWhen(fn () => request()->is('home')),
Section::make('Blog', route('blog.index'))
->activeWhen(fn () => request()->is('blog/*')),
];
});
Accessing Navigation: In a Blade view:
@foreach (Navigation::get('main') as $section)
<a href="{{ $section->url }}" class="{{ $section->isActive() ? 'active' : '' }}">
{{ $section->title }}
</a>
@endforeach
Dynamic Navigation Trees: Use closures to define navigation dynamically (e.g., based on user roles or permissions):
Navigation::make('admin', function () {
return auth()->user()->can('access-admin')
? [
Section::make('Dashboard', route('admin.dashboard')),
Section::make('Users', route('admin.users.index')),
]
: [];
});
Nested Sections: Build hierarchical navigation (e.g., dropdowns):
Navigation::make('sidebar', function () {
return [
Section::make('Products')
->addChildren([
Section::make('All Products', route('products.index')),
Section::make('Categories', route('products.categories')),
]),
];
});
Active State Logic: Customize active state checks with closures or methods:
Section::make('Profile', route('profile.show'))
->activeWhen(fn () => request()->routeIs('profile.*'));
Breadcrumbs: Define breadcrumbs separately and reuse sections:
Navigation::make('breadcrumbs', function () {
return [
Section::make('Home', route('home')),
Section::make('Blog', route('blog.index')),
Section::make('Post', route('blog.post', ['post' => 1])),
];
});
Blade Components: Create reusable components for rendering navigation:
<x-navigation.render :sections="Navigation::get('main')" />
// In a Blade component
public function render()
{
return view('components.navigation', [
'sections' => $this->sections,
]);
}
JavaScript Frameworks: Pass navigation data to Vue/React via Laravel Mix or Inertia.js:
// Inertia.js example
const navigation = @json(Navigation::get('main'));
API Responses: Expose navigation structure in API responses:
return response()->json([
'navigation' => Navigation::get('main')->toArray(),
]);
Caching Navigation: Navigation is cached by default (TTL: 1 hour). Clear cache when dynamically updating navigation:
php artisan cache:clear
Or disable caching in config/navigation.php:
'cache' => false,
Active State Conflicts:
Ensure activeWhen closures are mutually exclusive to avoid multiple sections appearing active.
Nested Section URLs: Child sections inherit the parent’s URL unless explicitly overridden. Test nested routes carefully.
Dependency Injection:
Avoid instantiating Navigation directly in controllers. Use dependency injection or the Navigation facade.
dd(Navigation::get('main')->toArray());
Navigation::clearCache();
Custom Section Classes:
Extend Spatie\Navigation\Section to add metadata:
class CustomSection extends Section
{
public function hasPermission(): bool
{
return auth()->user()->can($this->permission);
}
}
Middleware for Navigation: Dynamically modify navigation in middleware:
public function handle(Request $request, Closure $next)
{
Navigation::extend('main', function () {
return auth()->check() ? [...] : [];
});
return $next($request);
}
Localization: Use Laravel’s localization helpers for multilingual navigation:
Section::make(__('Home'), route('home'))
->locale('en')
->activeWhen(fn () => app()->getLocale() === 'en');
Testing: Mock navigation in tests:
Navigation::fake([
'main' => [
Section::make('Test', '#'),
],
]);
How can I help you explore Laravel packages today?