tomatophp/filament-menus
TomatoPHP Filament Menus is a flexible menu builder for Laravel and Filament apps. Create and manage menus with nested items in the admin panel, assign them to custom locations, and render dynamic navigation anywhere using a simple API.
Architecture fit. This is a narrow, single-purpose Filament plugin: a CRUD resource over a menu / menu_item schema, plus two render paths — a panel navigation loader (FilamentMenuLoader) that replaces ->navigationItems() in a PanelProvider, and a Blade/Livewire component (<x-filament-menu>) for embedding outside the panel. That shape is a good architectural fit only if navigation is a domain concept you actually want in the database. If menus are owned by engineering and change per deploy, this package adds a query, a cache-invalidation surface, and an ownership liability in exchange for capability you don't need. If marketing/ops need to restructure nav without a release, or you're standing up multiple branded panels/tenant sidebars from one schema, the fit is strong and the internal equivalent is roughly 5–10 dev days.
Integration feasibility. The install path is small and well-bounded: composer require, register the plugin in the panel provider, publish config/assets, migrate, then exercise the resource. A Filament-fluent developer reaches a working menu in about half a day; add half a day if filament-shield is in play. The real feasibility question is not effort, it's the version wall.
Technical risk.
lara-zeus/spatie-translatable ^2.0, a fork rather than upstream Spatie. If the app already ships spatie/laravel-translatable, expect duplicate service providers or constraint conflicts. Make that dependency call consciously at evaluation time, not during a release.filament-shield. That is visibility filtering over an existing policy layer, not a per-item ACL editor. On plain spatie/permission or bespoke RBAC, verify explicitly; the failure is a silent nav leak, which is minor here and a bad assumption to carry elsewhere.navigationItems() in place while enabling DB menus produces duplicated or shadowed items that are painful to diagnose. This is a process risk, and it's the most likely way a competent team ships a broken nav.Key questions to settle before committing.
Stack fit and gating. Gate on the stack first, and treat it as a hard gate. On Filament v3/v4 or Laravel ≤11 this is a non-starter unless an earlier major still receives fixes — budget a day of investigation before committing the team. On Filament v5 + Laravel 12/13 + PHP 8.2+, proceed.
Migration path. Migrate one surface at a time, and pick replace-or-augment deliberately. The recommended sequence:
MenuResolver (or view composer) on day one that takes a menu key, returns a cached tree, and is the only place package classes are imported. One hour of work, one-file swap if you later fork, pin an unreleased fix, or replace the package.main, add a route link and an external URL, drag into order, point the panel provider at that menu. This exercises the resource, ordering, link resolution, and the panel loader in one pass. Do it as a single deliberate commit, and audit navigationItems(), navigationGroups(), and every shouldRegisterNavigation() call in the same pass — those are code-level visibility decisions now competing with DB-level ones.footer menu and render it in a layout partial via <x-filament-menu>. Keep the component in one partial so the markup contract has a single home to re-check after upgrades.admin-main, client-{tenant}-sidebar, marketing-footer, resolved from panel/tenant context. Never hardcode a menu ID in a Blade file. This is where the package earns its keep across multiple shells.Compatibility work.
spatie/laravel-translatable providers and constraint conflicts before install, not after.FilamentMenuLoader namespace — it changed in v5.0.0 and stale imports surface as a confusing class-not-found rather than an obvious menu error.Sequencing and exit criteria. Phases 1–3 are the evaluation; treat them as a go/no-go gate. If both render paths work, the package fits and you commit to the adapter pattern. If either fights you, you've learned it for under two days. Phases 4–6 are productionization and should not start until the gate passes.
Maintenance. Plan to own this, modestly. Concretely: pin versions, vendor or fork rather than editing vendor files, keep package models confined to the adapter so a fork stays small, and re-verify published views and icon rendering after any theme change (the v5 icon fix is theme-coupled and a custom theme can regress it). If you add columns to the menu tables, update or wrap the shipped Menu/MenuItem factories instead of forking them in place and forgetting. Safe extension points are your resolver, published config and views, and a custom link-resolution step.
Support. There is no vendor SLA. The signals are mixed but net positive for a package this small: a real test suite, a CI matrix, and a first external contributor. None of that is a guarantee. The practical posture is: read the source before adopting (it's small — a resource plus renderers), sketch the fork plan before you need it, and keep the resolver boundary so replacing the package is a contained change.
Scaling. The schema scales fine; the operational surface is where scaling bites. Three things to decide up front:
Failure modes to instrument.
navigationItems() alongside DB menus. Prevented by the single-commit audit, not by monitoring.filament-shield. Verify explicitly against the real stack.Ramp-up. Half a day for a Filament-fluent developer to a working menu; another half-day with shield; the adapter, caching, and link-validation work is where the real effort sits and should be scoped as engineering, not configuration. The onboarding artifact that matters is a one-page runbook: where menus are authored, which menu keys map to which surfaces, how to dump a resolved tree, and how to invalidate cache. Note the honest ceiling too — this is authored against Filament's rendering conventions and is not a framework-neutral menu system for a public site. The Livewire component is an embedding escape hatch, not a general-purpose renderer.
How can I help you explore Laravel packages today?