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

Filament Menus Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Check the version wall before anything else. v5.0.0 requires Filament v5, Laravel 12/13, and PHP 8.2+. If you're on Filament v3 or v4, stop here and decide whether you're upgrading Filament first — the package is coupled to a young Filament major, and there's no benefit in fighting two upgrades at once.

Install in four moves:

composer require tomatophp/filament-menus
  1. Register the plugin in your PanelProvider using the snippet as shipped in the v5.0.0 README — both the plugin snippet and the FilamentMenuLoader namespace were corrected in that release, and older blog posts/forks will hand you a broken import.
  2. Publish config/views and run the package migrations.
  3. php artisan migrate.
  4. Open the panel and find the Menus resource.

Realistic estimate: half a day for a Filament-fluent dev to a working menu; add another half-day if you're installing filament-shield at the same time.

Read three things before writing integration code, in this order:

  • The Menus / Menu Items resource — this is the entire admin product surface: create menus, nest items, drag to reorder, pick a link type (route, page/model reference, custom URL), enter translated labels.
  • The two render paths — the panel navigation loader (FilamentMenuLoader) and the Blade/Livewire component (<x-filament-menu>). These are the only integration points most apps touch.
  • The migration and loader source. It's a small package: a CRUD resource over a menu/menu-item schema plus renderers. Read it before you commit, because adopting it means you may end up owning it.

First use case — replace one hardcoded panel nav. Create a menu keyed main, add a route link and an external URL, drag them into order, then point the panel at that menu instead of ->navigationItems([...]). That single swap exercises the resource, ordering, link resolution, and the panel loader in one pass. Then add a footer menu and render it in a Blade layout via <x-filament-menu> — that proves the frontend/Livewire path independently of the panel. If both work, the package fits. If either fights you, you've learned it cheaply.

Implementation Patterns

Wrap the package in your own resolver on day one. Don't import package classes from layouts or PanelProvider. Create a small MenuResolver or view composer that takes a menu key, returns a tree, and is the sole import site for the package.

// app/Support/MenuResolver.php
public function for(string $key): Collection
{
    return Cache::rememberForever("menu:{$key}", fn () => 
        \TomatoPHP\FilamentMenus\Models\Menu::query()
            ->where('key', $key)
            ->firstOrFail()
            ->items  // illustrative — read the source for the real relation
    );
}

This costs an hour and buys a one-file swap when you fork, replace, or pin an unreleased fix. Given the adoption profile (35 stars, zero dependents, single maintainer), it's the highest-leverage habit here.

Panel navigation mode. Replace ->navigationItems() and ->navigationGroups() with the loader pointed at a menu key, in a single deliberate commit. Half-migrated nav is the primary integration failure mode — you get duplicated or shadowed items that are genuinely painful to debug. In the same pass, audit every shouldRegisterNavigation() call: those are code-level visibility decisions that now compete with DB-level ones.

Standalone render mode. For storefront headers, portal sidebars, or a second panel, use <x-filament-menu> with the menu key, kept in a layout partial — not scattered through views. One home for the markup contract means one place to re-check after upgrades.

Keyed menus as your addressing scheme. Name menus admin-main, client-{tenant}-sidebar, marketing-footer. Resolve the key from the current panel/tenant in your resolver and pass it to the renderer. This is where the package earns its keep — one schema driving several branded shells — but only if you never hardcode a menu ID in a Blade file.

Centralize link resolution. External URLs, route names, and model references resolve through different code paths. Put URL generation in your adapter and add form-level validation (or an observer) so a stale route name or a model reference to a deleted record fails loudly in staging rather than shipping a dead nav item. If you only do one integration nicety, do this one.

Permission-aware nav. With filament-shield installed, items the current user may not see are skipped at render time — a real win over hand-rolling visibility. Keep the boundary in your head: this is filtering over your existing policy layer, not a per-item ACL editor. If you run plain spatie/permission + policies or a bespoke RBAC table, don't assume it generalizes — test it explicitly and filter in your resolver if it doesn't.

Cache deliberately, invalidate on write. Navigation moved from compile-time to query-time, which means it can be stale and wrong after a restore or bad seed. Cache the resolved tree and forget it in an observer on the menu/menu-item models. Don't skip caching "because the query is small" — skip it because you measured it.

Test the regressions that already bit someone. Use the shipped Menu and MenuItem factories for fixtures instead of hand-inserting rows. Assert two things in feature tests: the panel nav contains the expected 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 were the exact v5 crash cases, and they're the ones you'll reintroduce by publishing views.

Gotchas and Tips

  • The version wall is real. v5 targets a young Filament major. You're coupling to Filament 5's first-year churn — pin the package and Filament versions together and upgrade them as a pair.
  • The translatable dependency is a fork. Translations resolve to lara-zeus/spatie-translatable ^2.0, not upstream Spatie. If your app already ships spatie/laravel-translatable, check for duplicate service providers and constraint conflicts before treating this as drop-in. Make that supply-chain call consciously.
  • Shield-specific behavior isn't generic. "Skip items the user may not see" is scoped to filament-shield in the release notes. Verify against your actual authorization stack — a silent permission leak in nav is minor, but the same assumption applied elsewhere isn't.
  • Two sources of truth is the failure mode. Leaving navigationItems() in place while enabling DB menus produces shadowed or duplicated items. Pick replace-or-augment deliberately.
  • Upgrade break: FilamentMenuLoader namespace. It changed in v5.0.0. After upgrading, grep for the old import — a stale reference surfaces as a confusing class-not-found, not a menu error.
  • Published views can reintroduce the v5 fatals. Missing badges and missing locale translations used to crash the items table, <x-filament-menu>, and the loader. Fixed in v5 — but your published copies can reintroduce the same crashes. Re-check after every upgrade.
  • Icon sizing is theme-coupled. The v5 fix makes icons render correctly under Filament v5 themes; a custom theme can regress it. Eyeball icons after any theme change.
  • Don't treat it as a framework-neutral menu system for a public site. It's authored against Filament's rendering conventions — Filament-shaped markup, Tailwind/theme assumptions. The Livewire component is an escape hatch for embedding, not a general-purpose renderer.
  • Broken model references accumulate silently. Deleting a referenced record leaves a nav item pointing at nothing. Add a guard — soft deletes, morph-map discipline, or a scheduled integrity check — rather than hearing about it from a user.
  • Ordering and nesting are data, not schema. Drag-and-drop can produce deep or orphaned trees. Agree a nesting-depth guardrail with ops up front; the package gives you a builder, not editorial policy.
  • Debug "the nav is wrong" in one minute. Add a small artisan command or resolver log line that dumps the resolved tree for a key. Stale menus after a restore or bad seed are the characteristic DB-driven-nav failure, and this is the cheapest way to diagnose them.
  • Factories drift. If you add columns to the menu tables, update or wrap the shipped Menu/MenuItem factories rather than forking them in place and forgetting.
  • Know your extension points. Safe: your adapter/resolver, published config and views, a custom link-resolution step. Not safe: editing vendor files. Vendor or fork instead, and keep package models confined to your adapter so the fork stays small.
  • Ownership is the standing risk, not the code. The v5 test suite and the Laravel 12/13 × PHP 8.3/8.4 matrix genuinely lower the cost of owning it, and the first external contributor is a positive signal — but neither is a support guarantee. Pin, vendor, read the source, and have the fork plan sketched before you need it.
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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route