rinvex/laravel-menus
Rinvex Laravel Menus is a fluent, database-backed menu builder for Laravel. Create nested, sortable menus with route links, authorization checks, and Blade rendering helpers for flexible navigation.
Install with Composer:
composer require rinvex/laravel-menus
Laravel package discovery should register the service provider and Menu facade. If you disable discovery, register Rinvex\Menus\Providers\MenusServiceProvider and alias Rinvex\Menus\Facades\Menu as Menu.
Publish the package config first:
php artisan vendor:publish --provider="Rinvex\Menus\Providers\MenusServiceProvider"
Look first at:
config/rinvex.menus.php — default presenter and package options.src/Presenters/Bootstrap/ — built-in Navbar, Nav Pills, Nav Tabs, Sidebar, and Breadcrumb presenters.src/MenuBuilder.php, src/Menu.php, and src/MenuItem.php — the builder/item API you will use daily.Define your first menu in a service provider, usually AppServiceProvider::boot() or a dedicated MenuServiceProvider:
use Rinvex\Menus\Facades\Menu;
Menu::make('main', function ($menu) {
$menu->add('Home', ['route' => 'home']);
$menu->add('About', ['url' => '/about']);
});
Render it in Blade:
{!! Menu::get('main')
->setPresenter(\Rinvex\Menus\Presenters\Bootstrap\NavbarPresenter::class)
->render() !!}
Centralize menu definitions. Define menus in a service provider or a small dedicated menu class rather than in controllers or Blade templates. This keeps menus reusable across layouts, emails, and API-driven views, and avoids duplicate Menu::make() calls.
Use route/action/url options deliberately. The second argument to add() can be a URL string or an options array. Prefer explicit options so intent is clear:
$menu->add('Dashboard', ['route' => 'dashboard', 'icon' => 'fa fa-dashboard']);
$menu->add('Docs', ['url' => 'https://example.com/docs']);
Build hierarchy by nesting. Child items are added on the parent item:
$menu->add('Settings', '#')->add('Profile', ['route' => 'profile']);
$menu->add('Settings')->add('Billing', ['route' => 'billing']);
Use presenters as the styling boundary. Set a presenter per menu or globally in config. Built-in Bootstrap presenters cover common cases; for custom markup, extend a built-in presenter and override the rendering methods rather than manually looping menu items in Blade.
Separate data from HTML attributes. Use attr() for HTML attributes and data() for presenter-specific values:
$menu->add('Reports', ['route' => 'reports'])
->attr('class', 'reports-link')
->data('icon', 'fa fa-chart');
Handle active state explicitly where needed. Auto-detection is usually URL-based. For route-based menus, nested resources, or query strings, force activation:
$menu->add('Users', ['route' => 'users.index'])->activate();
Use a view composer for shared menus. Share the rendered menu or the menu object with layouts once, instead of resolving it in every Blade file:
View::composer('layouts.app', function ($view) {
$view->with('mainMenu', Menu::get('main'));
});
Cache static menus. Menu construction is cheap but not free. For large or permission-filtered menus, cache the rendered HTML or the menu definition and invalidate on deploy or permission changes. Avoid caching per-user menus without a user-specific key.
If using persistent menu models, publish migrations/models only if your installed version provides them, then treat the database as configuration and cache aggressively. The core package is primarily an in-memory builder.
render() returns markup; in Blade you must use {!! !!}. Escape user-generated titles or attribute values yourself, or you introduce XSS.add('Name', 'about') treats the second argument as a URL, not a route name. Use ['route' => 'about'] when you mean a named route.isActive() logic in a custom presenter or call activate() explicitly.setUp()/tearDown(), or avoid asserting on global menu state.Menu alias can collide with app models or other packages. Inject MenuBuilder or alias the facade explicitly in config/app.php.booted() or a dedicated provider if definitions run too early.data() is not attr(). Putting class or id in data() will not automatically render it as an HTML attribute unless your presenter reads it.getItemView, getItemLinkView, getItemAttributes, and child-item methods to change markup without rewriting menu definitions.How can I help you explore Laravel packages today?