Create Custom Navigation in Filament (Grouped vs Ungrouped Menus + RBAC)
James Manager
Filament PHP provides an excellent admin panel framework with automatic navigation generation. However, sometimes you need more control - especially when you want to position navigation groups alongside standalone items without sacrificing access control.
This guide walks through building a custom navigation system in Filament v4 while maintaining proper role-based visibility.
π§ The Challenge
Filament applies a strict and predictable ordering algorithm:
- Sort navigation items using navigationSort.
- Group them β items without groups automatically form an βungroupedβ section.
- Ungrouped items are always placed first, before grouped items.
This creates an issue when you want an order like:
- Dashboard (ungrouped)
- Requisitions (group)
- Inventory (group)
- Users (ungrouped)
- Settings (ungrouped)
β³ Filament will always place Dashboard, Users, and Settings before grouped items. You cannot mix them in between by default.
The Strategy: Custom Navigation Builder
Filament exposes a navigation() method in your PanelProvider, allowing full control of ordering. This means we can use anonymous groups (groups without labels) to position navigation stacks anywhere.
Step 1 β Define Custom Navigation
->navigation(function (NavigationBuilder $builder): NavigationBuilder {
return $builder
->groups([
NavigationGroup::make() // Dashboard
->items([
NavigationItem::make('Dashboard')
->icon('heroicon-o-home')
->isActiveWhen(fn (): bool => request()->routeIs('filament.app.pages.dashboard'))
->url(fn (): string => Dashboard::getUrl()),
]),
NavigationGroup::make('Requisitions')
->icon('heroicon-o-document-text')
->items([
...RequisitionResource::getNavigationItems(),
...InternalRequisitionResource::getNavigationItems(),
]),
NavigationGroup::make('Inventory')
->icon('heroicon-o-cube')
->items([
...InventoryItemResource::getNavigationItems(),
...InventoryReturnResource::getNavigationItems(),
...InventoryTransactionResource::getNavigationItems(),
]),
NavigationGroup::make() // Users + Settings
->items([
...UserResource::getNavigationItems(),
...Settings::getNavigationItems(),
]),
]);
});
β Problem β Permissions Break
When you manually call getNavigationItems(), you bypass:
β
shouldRegisterNavigation() β controls visibility
Β β
canAccess() β controls page access
Meaning:
The menu item will render even if the user should not see it.
Example:
NavigationGroup::make()
->items([
...UserResource::getNavigationItems(), // β always visible
...Settings::getNavigationItems(), // β always visible
])
Even if:
public static function shouldRegisterNavigation(): bool
{
return auth()->user()->hasRole('admin');
}
β¦it will still show.
Β So we must manually enforce permission checks.
β Full Solution β Conditional Navigation Items
->navigation(function (NavigationBuilder $builder): NavigationBuilder {
$groups = [
/* β¦ regular groups β¦ */
];
// Admin-only section
$adminItems = [];
if (UserResource::shouldRegisterNavigation()) {
$adminItems = array_merge($adminItems, UserResource::getNavigationItems());
}
if (Settings::shouldRegisterNavigation()) {
$adminItems = array_merge($adminItems, Settings::getNavigationItems());
}
if (count($adminItems) > 0) {
$groups[] = NavigationGroup::make()->items($adminItems);
}
return $builder->groups($groups);
});
π Permissions Setup
Example β Users Resource
class UserResource extends Resource
{
public static function shouldRegisterNavigation(): bool
{
return auth()->user()->hasRole('admin');
}
public static function canAccess(): bool
{
return auth()->user()->hasRole('admin');
}
}
Share this article
Related Articles
From Data Overload to Clarity: Why Your Business Needs a Custom Dashboard
Custom dashboards streamline decision-making with predictive analytics, interactive visuals, and smart alerts.
Custom Login & Logout Redirects in Filament v4
Learn how to set custom login and logout redirects in Filament v4, including intended URLs and panel-specific flows.
Troubleshooting Laravel Livewire: Resolving the 'Unable to Find Component' Error
Learn how to troubleshoot the "Unable to find component" error in Laravel Livewire. Follow our step-by-step guide to quickly resolve this common issue and get back to developing with ease.