agroezinger/filament-navigation-enhanced

Adds collapsible child-item support to the Filament sidebar navigation.

Maintainers

Package info

github.com/agroezinger/filament-navigation-enhanced

Homepage

Language:Blade

pkg:composer/agroezinger/filament-navigation-enhanced

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-06-05 20:53 UTC

This package is auto-updated.

Last update: 2026-06-05 21:21:06 UTC


README

Adds collapsible child-item support to the Filament v5 sidebar navigation. A parent item becomes an expand/collapse toggle; its children are revealed in an animated sub-list. The group auto-expands whenever a child is active.

image

Requirements

Dependency Version
PHP ^8.2
filament/filament ^5.0

Installation

Via Composer (Packagist)

composer require agroezinger/filament-navigation-enhanced

Publish the view override

The package ships a replacement for Filament's sidebar/item component. Publish it once so Filament picks it up:

php artisan vendor:publish --tag=navigation-enhanced-views

This copies the view to resources/views/vendor/filament-panels/components/sidebar/item.blade.php.

Usage

1. Register the plugin

Add NavigationEnhancedPlugin to every panel that should use the feature:

use Agroezinger\FilamentNavigationEnhanced\NavigationEnhancedPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugins([
            NavigationEnhancedPlugin::make(),
        ]);
}

The plugin itself has no configuration options — registering it documents intent and allows future configuration hooks.

2. Add the trait to a parent Page or Resource

use Agroezinger\FilamentNavigationEnhanced\Concerns\HasNavigationChildren;
use Filament\Navigation\NavigationItem;

class SettingsPage extends Page
{
    use HasNavigationChildren;

    protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-cog-6-tooth';
    protected static string|\UnitEnum|null $navigationGroup = 'Einstellungen';
    protected static ?string $navigationLabel = 'Einstellungen';

    public static function getNavigationChildItems(): array
    {
        return [
            NavigationItem::make('Allgemein')
                ->url(static::getUrl())
                ->icon('heroicon-o-cog-6-tooth')
                ->isActiveWhen(fn() => request()->routeIs('filament.club.pages.settings.general')),

            NavigationItem::make('Abteilungen')
                ->url(DepartmentResource::getUrl('index'))
                ->icon('heroicon-o-rectangle-stack')
                ->isActiveWhen(fn() => request()->routeIs('filament.club.resources.departments.*')),

            NavigationItem::make('Benutzer')
                ->url(UserResource::getUrl('index'))
                ->icon('heroicon-o-users')
                ->isActiveWhen(fn() => request()->routeIs('filament.club.resources.users.*')),
        ];
    }
}

3. Suppress child items from the top-level navigation

Resources and Pages that appear as children should not register themselves as standalone sidebar items. Override shouldRegisterNavigation() on the Resource:

class DepartmentResource extends Resource
{
    public static function shouldRegisterNavigation(): bool
    {
        return false;
    }
}

For a child Page, set:

protected static bool $shouldRegisterNavigation = false;

4. Active-state detection

Use request()->routeIs() with a wildcard to match all pages of a resource (index, create, edit):

NavigationItem::make('Abteilungen')
    ->isActiveWhen(fn() => request()->routeIs('filament.{panel}.resources.{resource-slug}.*')),

For a single Page, match the exact route name:

NavigationItem::make('Allgemein')
    ->isActiveWhen(fn() => request()->routeIs('filament.{panel}.pages.{page-slug}')),

To find the exact route names for your panel, run:

php artisan route:list --name="filament.{panel}" --json \
  | php -r "foreach(json_decode(file_get_contents('php://stdin'),true) as \$r) echo \$r['name'].PHP_EOL;"

How it works

The package publishes a custom sidebar/item Blade component that extends Filament's default one. When a NavigationItem carries child items (set via $item->childItems([...])), the component renders a <button> toggle instead of an <a> link, and wraps the children in an Alpine.js-powered <ul> with enter/leave transitions.

HasNavigationChildren::getNavigationItems() wraps the parent's items and calls childItems() on each with the return value of getNavigationChildItems(). The parent item auto-expands on load when $active || $activeChildItems is true.

Items without children fall through to Filament's original rendering path unchanged.

License

MIT