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

Menu Laravel Package

akaunting/menu

A Laravel package for building, managing, and rendering application menus. Define menu items and nested navigation structures in code, then output ready-to-use menus with flexible configuration for admin interfaces.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require akaunting/menu

Laravel auto-discovery registers Akaunting\Menu\MenuServiceProvider and the Menu facade alias on 5.5+. Publish the config so you can see the preset map:

php artisan vendor:publish --provider="Akaunting\Menu\MenuServiceProvider" --tag="menu"

Where to look first — the surface is small enough to read in one sitting:

  1. src/Menu.php — the named-menu registry (create, get, exists, render)
  2. src/Builder.php — the menu object itself (addItem, addDivider, addHeader, addSubMenu)
  3. src/Item.php — links, icons, attributes, children, active state
  4. resources/views/ — the preset Blade templates that render() emits
  5. config/menu.php — preset names (navbar, sidebar, dropdown)

First use case — a sidebar with a divider and a section header:

use Akaunting\Menu\Menu;

Menu::create('admin', function ($menu) {
    $menu->addItem('Dashboard', route('dashboard'), 'home');
    $menu->addItem('Reports', route('reports.index'), 'chart-bar');
    $menu->addDivider();
    $menu->addHeader('Settings');
    $menu->addItem('Users', route('users.index'), 'user');
});

Render in your layout:

{!! Menu::get('admin')->preset('sidebar')->render() !!}
{{-- or, if the registry proxy exists in your build: --}}
{!! Menu::render('admin') !!}

Confirm exact method names (make vs create, addItem vs add) against the README for the tagged version you install — the package was forked from lavary/laravel-menu and the vocabulary differs slightly across forks.

Implementation Patterns

Register once, decorate per request

Create the base menu in a service provider. Add auth- or tenant-dependent items in middleware or a view composer, not in boot() — under Octane or queue-driven rendering, the provider can run before a user is bound.

// app/Providers/MenuServiceProvider.php
public function boot(): void
{
    if (! Menu::exists('sidebar')) {
        Menu::create('sidebar', function ($menu) {
            $menu->addItem(__('Dashboard'), route('dashboard'), 'home');
        });
    }
}
// app/Http/Middleware/BuildMenu.php
public function handle($request, Closure $next)
{
    if (Menu::exists('sidebar') && $request->user()?->can('viewAny', User::class)) {
        Menu::get('sidebar')->addItem(__('Users'), route('users.index'), 'users');
    }

    return $next($request);
}

Conditional items by ability

Prefer omitting the item over rendering a greyed-out one. If your build exposes Item::authorize($ability, $arguments), use it — the check travels with the definition and the renderer drops it silently. Otherwise guard at registration time with Gate::allows().

Sub-menus and ordering

$menu->addItem('Settings', '#', 'cog')
     ->addSubMenu('Users', route('users.index'), 'users')
     ->addSubMenu('Roles', route('roles.index'), 'shield');

addItemBefore / addItemAfter reference siblings by title string, so the ordering API is only as stable as your labels. If the menu is multilingual, either register in deterministic code order or add explicit data('order', n) and sort inside a custom preset.

Active state

Item::isActive() compares the current request against the item's route() / url(). Use route() whenever possible — string matching against url() breaks behind sub-directory installs, proxies, and query strings. Wildcard route matching (->route('reports.*')) is the sane choice for section-level items; otherwise set ->active() / ->inactive() explicitly for edge cases like detail pages.

Custom preset

Drop a Blade view under resources/views/vendor/menu/ and register it in config/menu.php:

'presets' => [
    'sidebar' => 'menu::presets.sidebar',
    'tiles'   => 'menu::presets.tiles',
],

Then $menu->preset('tiles')->render(). This is the cleanest seam for diverging visually without touching vendor code — presets are just views, so you own everything inside them.

Frontend integration (Inertia / SPA)

Build the menu server-side and ship data, not HTML: iterate items into a plain array and pass it as a prop. Don't reuse the Blade presets on the client — they carry active-state markup that will be stale.

Modular registration

In a module/plugin architecture, each module's provider registers its own items and never assumes the parent exists:

if (Menu::exists('sidebar')) {
    Menu::get('sidebar')->addItemBefore('Users', 'Audit Log', route('audit.index'), 'file-text');
}

This keeps provider load order irrelevant, which matters once package discovery is in play.

Gotchas and Tips

  • Duplicate creation is the #1 crash. Calling Menu::create('sidebar', …) twice in a request throws (or silently resets, depending on version). Always guard with Menu::exists() or funnel creation through exactly one provider.

  • Don't escape the output. {!! !!}, never {{ }}. Conversely, don't call render() twice on the same builder — some presets mutate item attributes during rendering.

  • Caching HTML freezes active state and role filtering. If you cache, key on user_id (or role set) plus request()->path(). Caching the menu definition is only safe when nothing depends on auth.

  • Route resolution fails loudly. route('reports.index') inside a provider throws if the route isn't registered yet — and that takes down every page that renders the menu, not just the Reports page. Wrap optional-module routes in Route::has(...).

  • Icon names are passthrough strings. The package ships no icon set; Akaunting passes Font Awesome names. If your preset doesn't render the icon attribute, you're storing dead data and wondering why the sidebar looks bare.

  • Dividers and headers aren't links. Some presets emit <a> for every child; a leading addDivider() produces an orphan rule. They also count toward isEmpty()/count(), so a menu with only a header will render as non-empty.

  • Ordering by title is brittle. A single translation change to a label silently breaks addItemBefore. Keep labels stable, or move ordering into data() + a custom preset.

  • Facade collision. If lavary/laravel-menu is also installed, both register a Menu alias and you'll get confusing behavior. Remove one, or alias the other in config/app.php.

  • Maintenance posture. Last release October 2023, 37 stars. Stable but quiet — pin the version in composer.json. Bug fixes are realistically your fork, which is fine given the codebase is a few thousand lines.

  • Extension points worth knowing.

    • Macroable facade/builder: Menu::macro('admin', fn () => Menu::get('menu.admin')) for a shorter call site.
    • Bind your own Builder/Item subclass in the container if you need extra item properties — though data('key', 'value') usually covers it.
    • Custom presets in config are the intended seam for visual changes.
  • Debug in tinker, not the browser. Menu::get('sidebar')->toArray() (or iterating ->items) shows exactly what was registered and which item is flagged active — far faster than reading rendered HTML.

  • Testing strategy. Assert on registration for logic (menu contains "Users" for an authorized user, not for a guest) and keep one snapshot per preset to catch markup changes on upgrade. Avoid asserting on active classes via full-page HTTP tests; the URL matching is the brittle part and will make the suite noisy.

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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib