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.
Build vs. buy on navigation management. This package replaces a bespoke "menu builder" admin module — CRUD for menus and items, drag-and-drop nesting, route/model/external-link targets, translated labels, permission-aware rendering — with a drop-in Filament resource plus a Blade/Livewire renderer. Scoped honestly (translation plumbing, policy-based hiding, test seeding), the equivalent internal build lands around 5–10 dev days; the buy case strengthens as the build estimate gets more realistic, not less. At a 35-star, single-maintainer package the honest framing is "buy the code, budget for the ownership," not "buy the support."
Who owns navigation. The core decision this forces: does menu structure stay in code (navigationItems(), routes, hardcoded Blade partials) or move to the database so marketing/ops can restructure navigation without a deploy? If the answer is "yes, within guardrails," this is the enabler. Translated labels extend that to multi-locale ops teams, not just single-language ones.
Multi-panel and multi-tenant information architecture. Because menus are DB-backed and renderable both as panel navigation and as a Livewire component, the same records can drive a per-panel admin nav, a per-tenant sidebar, or a customer-portal header. Relevant when deciding between one admin shell and several branded/segmented panels — this makes "several" cheaper to validate. Keyed menus (admin-main, client-{tenant}-sidebar, marketing-footer) are the addressing scheme that makes this a one-schema/many-shells story rather than N copies.
Role-aware navigation as a config decision, not a build item. With filament-shield installed, items the current user may not see are skipped at render time. That converts tiered admin experiences and client-scoped panels from roadmap line items into an afternoon prototype — provided shield is your authorization stack. Treat it as visibility filtering over your existing policy layer, not a menu-level permission editor. You still own the policy design, and if you run plain spatie/permission or bespoke RBAC, verify the hook explicitly rather than assuming it generalizes.
Localization roadmap sequencing. Translated labels arrive via lara-zeus/spatie-translatable ^2.0 — a fork, not upstream Spatie. That's enough to ship a bilingual admin nav; it is not an editorial workflow — no per-locale review queue, no fallback management UI, no translator roles. The decision it forces: is translated navigation a v1 requirement or a phase-two extension? Answer that before scoping, because it drags in a supply-chain call on the translatable dependency at the same time.
CMS-adjacent bets, cheaply validated. "Let clients edit their own admin/site chrome" is usually the first increment of a CMS ask. This package lets you test that demand for near-zero cost before committing to Statamic or a Filament content plugin — good for de-risking the bet, less good as the destination.
Whether navigation is a shared domain concept or panel-specific. The Livewire angle lets one set of menu records feed a storefront header, portal sidebar, and admin nav. Useful when scoping whether "navigation" belongs to the platform team or to each surface. Related scoping call: "replace the hardcoded nav" vs. "augment it" — pick one deliberately, because leaving navigationItems() in place while enabling DB menus produces duplicated or shadowed items that are expensive to debug.
Governance and editorial policy, which the package deliberately does not decide. Drag-and-drop ordering and nesting are data, not schema — nothing stops a deep or orphaned tree. Deleting a referenced record leaves a nav item pointing at nothing. Nesting-depth guardrails, link-integrity checks, and "who is allowed to restructure nav" are policy decisions the PM owns; the package supplies a builder, not an editorial workflow.
Ownership and exit-cost decisions. Low adoption (35 stars, 0 dependents, single maintainer) means adoption implies an ownership commitment. But v5.0.0 shipped alongside a Filament major with new permission/rendering/factory/install tests and a Laravel 12/13 × PHP 8.3/8.4 matrix — a real maturity signal that lowers fork cost and raises upgrade confidence. Conclusion unchanged in kind: plan to vendor or fork, pin versions, read the source. The plan is just cheaper than it was. Concretely, that means budgeting an adapter/resolver layer on day one so the package is confined to one import site and a swap or fork stays a one-file change.
Consider it when:
filament-shield is your authorization stack and you want tiered/role-scoped admin nav quickly. That's the one integration where the package does something a hand-rolled build would take real time to get right.Look elsewhere when:
spatie/laravel-translatable and can't absorb a fork of the translatable plugin — check for duplicate service providers and constraint conflicts before calling this drop-in.Adoption criteria checklist (for the go/no-go conversation): stack matches v5.0.0 exactly · one pilot surface identified (panel nav or one Blade partial, not both at once) · adapter/resolver ownership assigned · version pinning agreed with engineering · fork-or-vendor decision minuted · nesting-depth and link-integrity guardrails defined · cache-and-invalidate-on-write accepted as part of the scope · published views re-checked on every upgrade.
Pilot framing that keeps the decision cheap: swap one hardcoded panel nav for a keyed menu, then render a second footer menu via <x-filament-menu> in a layout. That exercises the resource, ordering, link resolution, the panel loader, and the standalone renderer in about half a day for a Filament-fluent dev. If both paths work, the package fits; if either fights you, you've learned it before committing a sprint. Budget another half-day if shield lands at the same time.
Risks to write into the decision record, not just the backlog: stale nav after a restore or bad seed (add a debug command that dumps the resolved tree per key) · broken model references accumulating silently (soft deletes, morph-map discipline, or a scheduled integrity check) · icon sizing regressing under a custom theme · published/overridden views reintroducing the v5 crashes on missing badges and missing locale translations · factories drifting once you add columns (wrap them, don't fork them).
For executives — one paragraph: We can let marketing and ops restructure navigation across our admin panels, tenant portals, and storefront headers without a deploy, for roughly the cost of a week or two of engineering instead of a bespoke build. It's an MIT-licensed plugin that drops into the Filament admin framework we already use, and the newest release shipped with a real test suite and a current Laravel/PHP support matrix. The trade-off is honest: it's a small, single-maintainer open-source project, so we'd be buying the code and owning it — pinning versions, wrapping it in a thin adapter of our own, and keeping a fork option open. That's a normal posture for a component this narrow, and it's the reason the cost is a week or two rather than a quarter.
For engineering — the shape of the work: Install and register the plugin (use the README as shipped in v5.0.0 — the plugin snippet and FilamentMenuLoader namespace were both corrected there, and stale posts will hand you a broken import). Then, before writing integration code, put a MenuResolver or view composer in front of it as the sole import site, so the package is a one-file swap. Two render paths exist — the panel navigation loader and the <x-filament-menu> / Blade helper — and they're the only integration points most apps touch. Replace navigationItems()/navigationGroups() as a single deliberate commit and audit shouldRegisterNavigation() in the same pass, because half-migrated nav is the characteristic failure. Centralize link resolution (route names, model references, external URLs) in our adapter with validation so stale routes fail loudly in staging. Cache the resolved tree and invalidate in an observer on write. Use the shipped Menu/MenuItem factories for fixtures, and write two regression tests: panel nav contains the right items for a given user, and the Blade component renders for a menu whose items have no badge and no translation in the current locale — those are the exact v5 crash cases, and publishing views is how we'd reintroduce them.
For the sponsor/decision-maker — what we're asking for and what we're not: We're asking for a scoped pilot (one panel nav, one Blade menu, half a day to a day) plus an hour to install the resolver boundary — not a platform commitment. We're explicitly not buying a CMS, an editorial workflow, or per-item permissions; translated labels are supported, translation process is not. Success criteria: both render paths work, the resolver is the only import site, versions are pinned alongside Filament, and the fork plan is written down. If the pilot fights us, we stop for a day's cost.
The one-sentence version for a steering meeting: "Database-driven navigation for our Filament panels and portal surfaces, for about a week of engineering — we own the code, we pin the versions, and we can walk away cheaply if it disappoints."
How can I help you explore Laravel packages today?