datlechin / filament-menu-builder
Create and manage menus and menu items
Package info
github.com/datlechin/filament-menu-builder
pkg:composer/datlechin/filament-menu-builder
Requires
- php: ^8.3
- filament/filament: ^5.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^10.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- pestphp/pest-plugin-livewire: ^4.0
Suggests
- spatie/laravel-translatable: For enhanced translatable model support with HasTranslations trait
- dev-main
- v1.0.0
- v0.7.0
- v0.6.6
- v0.6.5
- v0.6.4
- v0.6.3
- v0.6.2
- v0.6.1
- v0.6.0
- v0.5.4
- v0.5.3
- v0.5.2
- v0.5.1
- v0.5.0
- v0.4.0
- v0.3.1
- v0.3.0
- v0.2.0
- v0.1.1
- v0.1.0
- dev-depfu/update/composer/orchestra/testbench-11.0.0
- dev-depfu/check/composer/filament/filament-5.4.1
- dev-depfu/update/npm/tailwindcss-4.2.2
- dev-depfu/update/npm/@tailwindcss/cli-4.2.2
This package is auto-updated.
Last update: 2026-03-24 08:35:09 UTC
README
A Filament plugin for building menus with drag-and-drop ordering, nesting, custom links, and dynamic panels.
Requirements
- PHP 8.3+
- Filament 5.0+
- Laravel 12+
Upgrading
From v0.7.x (Filament v3) to v1.x (Filament v5)
- Update your
composer.json:
composer require datlechin/filament-menu-builder:^1.0
- Publish and run the new migration to add the
panel,icon, andclassescolumns:
php artisan vendor:publish --tag="filament-menu-builder-migrations"
php artisan migrate
The upgrade migration checks for existing columns before adding them, so it's safe on fresh installs too.
- Re-publish the config file if you published it previously:
php artisan vendor:publish --tag="filament-menu-builder-config" --force
Installation
Install via Composer:
composer require datlechin/filament-menu-builder
Publish and run the migrations:
php artisan vendor:publish --tag="filament-menu-builder-migrations"
php artisan migrate
Optionally, publish the config file:
php artisan vendor:publish --tag="filament-menu-builder-config"
Or use the install command:
php artisan filament-menu-builder:install
Usage
Register the plugin in your panel provider:
use Datlechin\FilamentMenuBuilder\FilamentMenuBuilderPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ FilamentMenuBuilderPlugin::make(), ]); }
Locations
Locations define where menus appear in your application:
FilamentMenuBuilderPlugin::make() ->addLocations([ 'header' => 'Header', 'footer' => 'Footer', ])
Menu Panels
Panels provide item sources for menus, either from Eloquent models or static lists.
Model Panel
Implement MenuPanelable on your model:
use Datlechin\FilamentMenuBuilder\Contracts\MenuPanelable; class Page extends Model implements MenuPanelable { public function getMenuPanelTitle(): string { return $this->title; } public function getMenuPanelUrl(): string { return route('pages.show', $this); } public function getMenuPanelName(): string { return 'Pages'; } }
Then register it:
use Datlechin\FilamentMenuBuilder\MenuPanel\ModelMenuPanel; FilamentMenuBuilderPlugin::make() ->addMenuPanels([ ModelMenuPanel::make() ->model(Page::class), ])
Static Panel
use Datlechin\FilamentMenuBuilder\MenuPanel\StaticMenuPanel; FilamentMenuBuilderPlugin::make() ->addMenuPanels([ StaticMenuPanel::make() ->name('pages') ->add('Home', '/') ->add('About', '/about') ->add('Contact', '/contact'), ])
add() also accepts target, icon, and classes:
StaticMenuPanel::make() ->name('social') ->add('GitHub', 'https://github.com', target: '_blank', icon: 'heroicon-o-code-bracket') ->add('Twitter', 'https://twitter.com', target: '_blank', classes: 'text-blue-500')
Custom Link & Custom Text Panels
The custom link panel is shown by default. The custom text panel (for non-link items like headings) is opt-in:
FilamentMenuBuilderPlugin::make() ->showCustomLinkPanel(true) ->showCustomTextPanel(true)
Custom Fields
Add extra fields to the menu or menu item forms:
use Filament\Forms\Components\TextInput; FilamentMenuBuilderPlugin::make() ->addMenuFields([ TextInput::make('description'), ]) ->addMenuItemFields([ TextInput::make('badge'), ])
Singular methods work too:
FilamentMenuBuilderPlugin::make() ->addMenuField(TextInput::make('description')) ->addMenuItemField(TextInput::make('badge'))
Multiple calls are merged, so fields registered from different service providers won't overwrite each other.
Customizing Navigation
FilamentMenuBuilderPlugin::make() ->navigationLabel('Menus') ->navigationGroup('Content') ->navigationIcon('heroicon-o-bars-3') ->navigationSort(3) ->navigationCountBadge(true)
Indent / Unindent
Nesting via indent/unindent actions is enabled by default:
FilamentMenuBuilderPlugin::make() ->enableIndentActions(true)
Translatable Menus
Built-in multilingual support with no extra packages required. Translatable fields are stored as JSON with locale tabs in the form UI.
Setup
- Enable translatable with your locales:
FilamentMenuBuilderPlugin::make() ->translatable(['en', 'nl', 'vi'])
- Publish and run the migration to convert columns from
stringtojson:
php artisan vendor:publish --tag="filament-menu-builder-translatable-migrations"
php artisan migrate
Existing string data is wrapped in the default locale (en). Edit $defaultLocale in the published migration to change this.
Configuring Translatable Fields
Only MenuItem.title is translatable by default:
FilamentMenuBuilderPlugin::make() ->translatable(['en', 'nl', 'vi']) ->translatableMenuItemFields(['title']) // default ->translatableMenuFields(['name']) // opt-in: make Menu name translatable too
Rendering Translated Titles
Use resolveLocale() in Blade to display titles in the current locale:
@foreach($menu->menuItems as $item) <a href="{{ $item->url }}"> {{ $item->resolveLocale($item->title) }} </a> @endforeach
resolveLocale() returns the translation for app()->getLocale(), falls back to the first available translation, or returns the raw string for non-translatable setups.
Spatie Translatable Compatibility
The JSON format is compatible with Spatie Laravel Translatable. If you add HasTranslations to a custom model, the plugin detects it and defers to Spatie's mutators.
use Spatie\Translatable\HasTranslations; class CustomMenuItem extends MenuItem { use HasTranslations; public array $translatable = ['title']; }
Custom Models
Replace the default models with your own:
FilamentMenuBuilderPlugin::make() ->usingMenuModel(CustomMenu::class) ->usingMenuItemModel(CustomMenuItem::class) ->usingMenuLocationModel(CustomMenuLocation::class)
Rendering Menus
Retrieve a menu by location. Results are cached and automatically busted on changes:
use Datlechin\FilamentMenuBuilder\Models\Menu; $menu = Menu::location('header');
Render menu items:
@if($menu) <nav> <ul> @foreach($menu->menuItems as $item) <li class="{{ $item->classes }} {{ $item->isActive() ? 'active' : '' }}"> @if($item->url) <a href="{{ $item->url }}" target="{{ $item->target }}" @if($item->rel) rel="{{ $item->rel }}" @endif> {{ $item->resolveLocale($item->title) }} </a> @else <span>{{ $item->resolveLocale($item->title) }}</span> @endif @if($item->children->isNotEmpty()) <ul> @foreach($item->children as $child) <li> <a href="{{ $child->url }}">{{ $child->resolveLocale($child->title) }}</a> </li> @endforeach </ul> @endif </li> @endforeach </ul> </nav> @endif
Active State Detection
Check if a menu item matches the current URL:
$item->isActive(); // exact URL match $item->isActiveOrHasActiveChild(); // matches self or any descendant
MenuItem Properties
| Property | Type | Description |
|---|---|---|
title |
string|array | The display title (array when translatable) |
url |
?string | The URL (null for text-only items) |
target |
string | Link target (_self, _blank, etc.) |
icon |
?string | Icon identifier (e.g. heroicon-o-home) |
classes |
?string | CSS classes for the item |
rel |
?string | Link rel attribute (e.g. nofollow noopener) |
type |
string | Panel name / source type (accessor) |
children |
Collection | Nested child items |
Testing
composer test
Changelog
See CHANGELOG for recent changes.
License
MIT License. See LICENSE.md.
