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 Navigation Laravel Package

van-ons/filament-navigation

Filament Navigation is a Filament plugin that adds a Navigation resource for building structural navigation menus with a clean drag-and-drop UI. Install via Composer, run migrations, publish assets, and register the plugin in your panel provider.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm your Filament major first — it decides which line you install. The package has two hard-coupled lines: ^2.0 supports Filament v4+, and the v1 line supports Filament ≤v3. The v2 release dropped v3 rather than spanning both, so this is not a "pick latest" decision.

composer require van-ons/filament-navigation:^2.0
php artisan migrate
php artisan filament:assets

Then register the plugin in one panel provider — see Implementation Patterns for why:

use VanOns\FilamentNavigation\FilamentNavigation;

return $panel->plugin(FilamentNavigation::make());

Where to look first, in order:

  1. docs/compatibility.md — the version matrix. This is the page that will break your build, not the README.
  2. vendor/van-ons/filament-navigation/src/ — the surface is small (resource, model, pages, a drag-and-drop field). Read the model and the resource before reading anything else; the handle column and the JSON items column are the entire contract you care about.
  3. UPGRADING.md — read it before you need it, so you know what a Filament v5 bump will look like.

First use case: create a main menu in the admin UI with three links and one nested child, then render it in a Blade partial. That single loop — author in Filament, persist as JSON, render yourself — is the whole package. If that works end-to-end, you understand the package.

Implementation Patterns

Register the plugin in exactly one admin panel. Registering it everywhere means a menu builder appears in panels where site chrome makes no sense. Cosmetic placement is configured on the resource statically:

use VanOns\FilamentNavigation\Filament\Resources\NavigationResource;

NavigationResource::navigationGroup('Content');
NavigationResource::navigationSort(10);
NavigationResource::navigationIcon('heroicon-o-bars-3');

The package authors; you render. This is the single most important pattern to internalize. You get a menu builder and a JSON tree. Recursive Blade rendering, active-state logic, caching, and cache invalidation are yours. Budget for writing a recursive partial:

{{-- resources/views/partials/nav.blade.php --}}
@props(['items', 'depth' => 0])
<ul @class(['nav', "nav--depth-{$depth}"])>
    @foreach ($items as $item)
        <li @class(['nav__item', 'is-active' => url()->current() === url($item->url)])>
            <a href="{{ url($item->url) }}">{{ $item->label }}</a>
            @if (! empty($item->children))
                @include('partials.nav', ['items' => $item->children, 'depth' => $depth + 1])
            @endif
        </li>
    @endforeach
</ul>

Note the recursive @include rather than a component — components re-resolve props per level and get awkward at depth.

Look up by handle, and wrap the lookup once. The handle column is the stable contract between your template and the editor's work. Confirm the model's FQCN from the resource's $model property or the vendor tree before hardcoding it, then centralize the lookup so a rename costs one edit:

$menu = \VanOns\FilamentNavigation\Models\Navigation::query()
    ->where('handle', 'main')
    ->first();

Cache the tree, invalidate on save. A nav menu is read on every page render and written maybe monthly — the textbook Cache::rememberForever case. Invalidate via a model observer on saved/deleted rather than a TTL, so editors see their change immediately:

Navigation::saved(fn ($nav) => Cache::forget("nav:{$nav->handle}"));
Navigation::deleted(fn ($nav) => Cache::forget("nav:{$nav->handle}"));

Cache the rendered shape (the item DTOs or an array), not the Eloquent model with its JSON cast — a schema change on deploy will deserialize stale payloads into a model you've since edited.

Use one row per menu. Header, footer, and legal links are three rows with three handles, not one row with a type discriminator. It keeps the editor's mental model clean and keeps cache keys trivial.

Gate editing with a policy. Filament's default policy scaffolding is the only authorization surface here; if non-admin roles can reach the panel, write a NavigationPolicy so site chrome isn't editable by whoever can log in.

Seed handles, not content. Seed an empty main/footer row in DatabaseSeeder so a fresh environment renders instead of throwing, and let editors own the items. Check whether the migration is publishable before you assume you can alter the table.

Gotchas and Tips

  • Forgetting php artisan filament:assets is the classic first-run failure. The drag-and-drop UI depends on published JS (SortableJS ships in the vendor tree). The symptom is a menu builder that renders but won't drag, with no console-visible cause. Run it after every deploy that touches the package.
  • Do not trust form-level uniqueness for handle. Verify whether the uniqueness is a database constraint or only a form rule before you build seeding or template contracts on it. If it's form-only, add the index yourself.
  • Expect a hard major bump at Filament v5. There is no graceful deprecation path here — the v2 release is the proof. Pin the major in composer.json and treat every Filament upgrade as a coordinated two-package change.
  • Adoption is genuinely thin (10 stars, 0 dependents, a release history dominated by Dependabot and Tailwind bumps). The mitigating factor is that the surface is small, MIT-licensed, and fully readable in vendor/ — forking or vendoring is a realistic exit. Price that in, don't assume it away. Practically: you will be among the first to hit nested-reorder bugs and Filament minor-version breakage, so write your own feature test over your Blade partial. The package's composer test covers the package, not your rendering.
  • Patch releases are near-zero-risk. Given what the changelogs contain, bumping 2.0.x → 2.0.y is safe. Save the caution for major bumps.
  • Debug by reading the JSON column directly, not the UI. SELECT items FROM navigations WHERE handle = 'main' tells you the exact tree shape the editor produced — that shape, not the docs, is the source of truth for your renderer.
  • No i18n modelling. If you need per-locale menus, that's one handle per locale (main_en, main_nl) or your own layer on top; the package doesn't ship it.
  • Extension point to prefer: the resource's static configuration methods and a policy. Avoid subclassing the pages unless you must — that's the code most likely to churn between minor versions.
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