Weave Code
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

Laravel Menu Laravel Package

spatie/laravel-menu

Build HTML menus in Laravel with a fluent API. Generate links via routes/actions/URLs, add classes and attributes, mark active items from the current request, and define reusable menu macros. Easy to render in Blade and customize output.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require spatie/laravel-menu
    

    Publish config (if needed) with:

    php artisan vendor:publish --provider="Spatie\Menu\MenuServiceProvider"
    
  2. First Menu: Define a macro in a service provider (e.g., AppServiceProvider):

    use Spatie\Menu\Menu;
    use Spatie\Menu\MenuItem;
    
    Menu::macro('main', function () {
        return Menu::new()
            ->add('Home', 'home')
            ->add('About', 'about')
            ->add('Contact', 'contact')
            ->setActiveFromRequest();
    });
    
  3. Render in Blade:

    <nav>
        {!! Menu::main() !!}
    </nav>
    

First Use Case: Basic Navigation

Create a simple top-bar menu with active state detection:

Menu::macro('topBar', function () {
    return Menu::new()
        ->action('HomeController@index', 'Home')
        ->action('ProductsController@index', 'Products')
        ->action('CartController@index', 'Cart')
        ->setActiveFromRequest();
});

Blade Output:

<ul>
    {!! Menu::topBar() !!}
</ul>

Implementation Patterns

1. Menu Composition

Nested Menus:

Menu::macro('admin', function () {
    return Menu::new()
        ->add('Dashboard', route('admin.dashboard'))
        ->add('Users', route('admin.users.index'))
        ->add('Settings', route('admin.settings.index'))
        ->add('Reports')
            ->add('Sales', route('admin.reports.sales'))
            ->add('Analytics', route('admin.reports.analytics'));
});

Conditional Items:

Menu::macro('user', function () {
    return Menu::new()
        ->addIfCan('Profile', 'view profile', route('profile.show'))
        ->addIfCan('Settings', 'edit settings', route('settings.edit'))
        ->add('Logout', route('logout'));
});

2. Dynamic Data Sources

From Database:

use App\Models\NavigationItem;

Menu::macro('dynamic', function () {
    return Menu::new()
        ->fromModel(NavigationItem::query(), 'title', 'url')
        ->setActiveFromRequest();
});

From API:

Menu::macro('apiMenu', function () {
    $items = Http::get('https://api.example.com/navigation')->json();
    return Menu::new()->fromCollection($items, 'name', 'href');
});

3. URL Generation

Leverage Laravel helpers:

Menu::macro('routes', function () {
    return Menu::new()
        ->toRoute('home', 'Home')
        ->toRoute('products.index', 'Products')
        ->toAction([ProductController::class, 'index'], 'Products')
        ->toUrl('/contact', 'Contact');
});

4. Customization

Add Classes/Attributes:

Menu::macro('styled', function () {
    return Menu::new()
        ->add('Home', route('home'))
            ->class('active')
            ->attr(['data-testid' => 'home-link']);
});

Blade Directives: Extend with custom directives:

// Register in AppServiceProvider
Blade::directive('menu', function ($expression) {
    return "<?php echo \\Spatie\\Menu\\Menu::{$expression}(); ?>";
});

Usage:

<nav>
    @menu('main')
</nav>

5. Caching

Cache menus for performance:

Menu::macro('cached', function () {
    return cache()->remember('menu.main', now()->addHours(1), function () {
        return Menu::new()
            ->add('Home', route('home'))
            ->add('About', route('about'));
    });
});

6. Middleware Integration

Dynamic menus via middleware:

// app/Http/Middleware/SetMenu.php
public function handle(Request $request, Closure $next) {
    $request->merge(['menu' => Menu::macro('user')]);
    return $next($request);
}

Blade Access:

{!! $menu !!}

Gotchas and Tips

Pitfalls

  1. Active State Conflicts:

    • setActiveFromRequest() uses Laravel’s request()->is() method. Override with custom logic:
      ->setActiveFromRequest(function ($route) {
          return $route->current() || request()->is('admin/*');
      });
      
  2. Nested Menu Indentation:

    • Default HTML structure may not match your CSS. Customize with:
      ->setHtmlAttribute('class', 'dropdown-menu')
      ->setHtmlAttribute('style', 'margin-left: 20px;');
      
  3. Macro Overwriting:

    • Macros are global. Use namespaced macros or a dedicated service class:
      class MenuService {
          public static function admin() { ... }
      }
      
  4. PHP 8+ Syntax:

    • Methods like addIfCan use callable syntax:
      ->addIfCan('Delete', fn() => auth()->user()->can('delete'), route('delete'));
      
  5. Blade Escaping:

    • Use {{ !! Menu::macro() !! }} to escape HTML properly.

Debugging Tips

  1. Inspect Menu Structure:

    dd(Menu::macro('main')->toHtml());
    

    or dump the raw object:

    dd(Menu::macro('main')->getItems());
    
  2. Check Active State:

    Menu::macro('main')->setActiveFromRequest();
    dd(Menu::macro('main')->getActiveItem());
    
  3. Validate URLs:

    • Use route() helpers to ensure URLs are resolvable:
      if (! route('home')) {
          throw new \RuntimeException('Route "home" not defined.');
      }
      

Extension Points

  1. Custom Menu Items: Extend Spatie\Menu\MenuItem:

    class CustomMenuItem extends MenuItem {
        public function setBadge($text) {
            $this->html .= "<span class='badge'>{$text}</span>";
            return $this;
        }
    }
    

    Usage:

    Menu::macro('notifications', function () {
        return Menu::new()->add(new CustomMenuItem('Alerts', '#'))
            ->setBadge('3');
    });
    
  2. Override Default HTML: Replace the default renderer:

    Menu::macro('customRenderer', function () {
        return Menu::new()
            ->add('Home', route('home'))
            ->setRenderer(function ($items) {
                return "<div class='custom-menu'>" . implode('', $items) . "</div>";
            });
    });
    
  3. Integrate with Laravel Breeze/Jetstream: Dynamically add auth-based items:

    Menu::macro('authMenu', function () {
        return Menu::new()
            ->addIf(auth()->check(), 'Profile', route('profile.show'))
            ->addIf(auth()->check(), 'Logout', route('logout'));
    });
    

Performance Quirks

  1. Avoid Over-Caching:

    • Cache menus at the macro level (not per-request) to prevent stale data:
      Menu::macro('cached', function () {
          return cache()->rememberForever('menu.main', function () {
              return Menu::new()->add('Home', route('home'));
          });
      });
      
  2. Lazy-Load Heavy Menus:

    • Defer dynamic menus until needed:
      Menu::macro('lazyMenu', function () {
          return function () {
              return Menu::new()->fromModel(NavigationItem::query());
          };
      });
      

Testing

  1. Mock Menus in Tests:

    Menu::macro('testMenu', function () {
        return Menu::new()->add('Test', '#');
    });
    
    $this->assertStringContainsString('Test', Menu::testMenu()->toHtml());
    
  2. Test Active States:

    $menu = Menu::macro('testMenu')->setActiveFromRequest();
    $this->assertEquals('active', $menu->getActiveItem()->getHtmlAttribute('class'));
    
  3. Verify Conditional Items:

    $this->actingAs($user)->get('/')->assertSee('Profile');
    $this->actingAs($guest)->get('/')->assertDontSee('Profile');
    
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.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai