Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Illuminate Menu Laravel Package

kalnoy/illuminate-menu

A fluent menu builder for Laravel: define nested menus in PHP and render them as HTML. Items support attributes, active-state detection, ordering, and custom views, giving you a clean, reusable API for building navigation in views or service providers.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require kalnoy/illuminate-menu

On Laravel 5.5+ the Kalnoy\Menu\MenuServiceProvider is auto-discovered. On older versions add it to config/app.php and register the facade alias:

'providers' => [Kalnoy\Menu\MenuServiceProvider::class],
'aliases'   => ['Menu' => Kalnoy\Menu\Facade::class],

Publish the config if the package ships one (php artisan vendor:publish --provider="Kalnoy\Menu\MenuServiceProvider") — this is where the active-item / URL-comparison settings live.

Where to look first (in this order)

  1. README.md — short, but it is the only real documentation for this package.
  2. config/menu.php — the active-detection settings that decide which item gets the active class.
  3. src/Builder.php — the fluent API surface (add, dropdown, raw, render).
  4. src/Item.php and the traits it composes (HasUrl, HasTitle, HasIcon, HasAttributes, Activatable) — this is where every ->icon() / ->attributes() / ->active() call actually resolves.

First use case: a navbar in a service provider

// app/Providers/AppServiceProvider.php (boot)
Menu::make('navbar', function ($menu) {
    $menu->add('Home', '/');
    $menu->add('About', route('about'));
    $menu->dropdown('Products', function ($sub) {
        $sub->add('Plans', route('plans'));
        $sub->add('Pricing', route('pricing'));
    });
});
{!! Menu::get('navbar') !!}

That is the whole loop: define once, render anywhere. The package emits Bootstrap 3 nav markup (nav navbar-nav, dropdown-menu, active) so the first thing to verify is that it looks right against your actual Bootstrap version.

Implementation Patterns

Define menus in one place, not in views. Keep a dedicated MenuServiceProvider (or a Menu bootstrapper class) rather than scattering Menu::make() calls across controllers. Views then contain a single {!! Menu::get('navbar') !!} line and are trivially reusable in layouts and mail/partials.

Prefer route() names over hardcoded URLs. Active-state detection works by comparing the current request against each item's URL. Using named routes keeps the comparison stable when you change paths, and it makes the menu testable ($this->get(route('pricing'))).

Nest with dropdown() and keep the closure flat. Nested dropdowns render, but Bootstrap 3's markup doesn't support submenus well, so treat it as one level. If you need deeper trees, build the structure elsewhere and only use this package for the top-level chrome.

Authorization-aware menus. Filter at build time inside the closure — it keeps the rendered markup free of items the user can't reach:

if (Gate::allows('view-reports')) {
    $menu->add('Reports', route('reports'));
}

Because the builder runs per request, this is safe but also the main performance consideration (see Gotchas).

Explicit active overrides for non-obvious pages. Pages reached from a parent nav entry (e.g. /products/42) usually won't light up the parent link. Use ->active() on the item, or widen active matching in config/menu.php so a prefix matches.

Localize titles at build time. $menu->add(__('nav.home'), route('home')) — titles are escaped on output, so passing translated strings is fine but passing HTML is not (see raw() below).

Compose presentation tweaks at the item level. ->icon('fa fa-home'), ->attributes(['class' => 'nav-item']) keep Bootstrap-class customisation out of your Blade templates; if you find yourself doing this repeatedly, write a small helper that maps "menu item spec" arrays to builder calls.

Testing. Menus are just state on the container; in a feature test assert on the rendered string:

$this->get('/')->assertSee('nav navbar-nav', false);
// or build directly:
$this->assertTrue(Menu::exists('navbar'));

Gotchas and Tips

  • Bootstrap version mismatch is the #1 friction. The package's markup targets Bootstrap 3. On Bootstrap 4/5 you'll get navbar-nav items without the expected nav-item / nav-link classes and a broken dropdown toggle. Either override the presentational classes via item attributes plus a little CSS, or extend/replace the renderer. Budget an hour for this before committing to the package.
  • Active detection is URL/route-string matching, not route-name matching. Query strings, trailing slashes, locale prefixes (/en/about vs /about) and the home route (/) are all classic false-negative sources. Check config/menu.php first, then fall back to ->active() for special cases.
  • Menus are rebuilt on every request. Building in a service provider means the closure runs for every page, including API routes and queue workers. If the closure does anything expensive (DB lookups, permission checks against many models), move the Menu::make() call into a view composer that only runs for the layouts that render the menu, or memoize the expensive parts.
  • raw() bypasses escaping. add() escapes the title; use raw() only for markup you fully control (icons, counters, badges). Never feed user input to it.
  • Facade alias collisions. Menu is a very generic alias; if another package or a legacy app class claims it, resolve via app('menu') or the fully-qualified facade instead.
  • Config caching. After php artisan config:cache, edits to published menu config won't take effect until you re-cache. If active states behave "stale", this is usually why.
  • Low adoption means you are the QA. With ~24 stars and no dependents, expect no upstream fixes for new Laravel versions. Pin the version in composer.json, and if you hit a fatal on a modern Laravel (return types, container signatures), the fastest path is vendoring/forking src/Builder.php and src/Item.php — they're small. spatie/laravel-menu and lavary/laravel-menu are the fallbacks if you need maintained alternatives.
  • Debugging tip. Menu::get('navbar')->getItems() (or dd(Menu::get('navbar'))) gives you the built tree with resolved URLs and active flags — far faster than reasoning about the final HTML string. Menu::exists('navbar') guards against the "menu not defined in this request" blank-render case, which typically happens when a provider is conditionally registered or the view is rendered outside the HTTP stack.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk