Adding a Filament Panel
The starter ships with a single Filament panel, the Administration panel at /administration, for back-end staff tooling. If your application involves CRUD tables, data exports, filterable lists, info displays, or action-driven workflows, creating a separate Filament panel is often the most productive path.
When to Use a Separate Panel
The Bootstrap-based user-facing stack (Blade + Northwestern Laravel UI) is the right choice for public landing pages, branded content, and custom interfaces. For authenticated, data-driven experiences involving tables, forms, filters, and bulk actions, Filament’s component library saves development time.
Common use cases for a second panel:
- Student or applicant portals - review status, upload documents, view decisions
- Faculty/staff dashboards - manage courses, events, or departmental data
- Reviewer or coordinator tools - evaluate submissions, assign reviewers, track progress
- Self-service data management - authenticated users managing their own records
Creating a New Panel
-
Generate the panel provider
Terminal window php artisan make:filament-panel portalThis creates a new provider at
app/Providers/Filament/PortalPanelProvider.phpand registers it automatically. -
Define a panel ID constant
Follow the pattern from
AdministrationPanelProvider. Define a publicIDconstant and reference it consistently:app/Providers/Filament/PortalPanelProvider.php class PortalPanelProvider extends PanelProvider{public const string ID = 'portal';public function panel(Panel $panel): Panel{return $panel->id(self::ID)->path(self::ID)// ...}} -
Configure the middleware stack
Your new panel needs the same middleware foundation as the Administration panel. At minimum, include the
InjectLivewireAssetsmiddleware, which resolves a conflict between the Bootstrap stack’s bundled Livewire assets and Filament’s asset injection:->middleware([InjectLivewireAssets::class,EncryptCookies::class,AddQueuedCookiesToResponse::class,StartSession::class,ShareErrorsFromSession::class,VerifyCsrfToken::class,SubstituteBindings::class,DisableBladeIconComponents::class,DispatchServingFilamentEvent::class,])->authMiddleware([Authenticate::class,]) -
Configure SPA mode
SPA mode gives Filament a single-page-app feel with client-side navigation. If you enable it, exclude the authentication and impersonation routes so full page loads occur for those flows:
->spa()->spaUrlExceptions([url('/auth/*'),url('/impersonate/*'),]) -
Set up resource discovery
Create a dedicated directory for your panel’s resources and point the panel at it. This keeps resources cleanly separated from the Administration panel:
->discoverResources(in: app_path('Filament/Portal/Resources'), for: 'App\Filament\Portal\Resources')->discoverPages(in: app_path('Filament/Portal/Pages'), for: 'App\Filament\Portal\Pages')->discoverWidgets(in: app_path('Filament/Portal/Widgets'), for: 'App\Filament\Portal\Widgets') -
Add optional features
Consider which features from the Administration panel you want to carry over:
->databaseNotifications()->databaseNotificationsPolling('30s')->globalSearch()->globalSearchDebounce('500ms')->plugins([NorthwesternTheme::make()->impersonationBanner()->withoutAssetRegistration(),])
Gating Access
Update canAccessPanel()
The User::canAccessPanel() method uses a match statement that intentionally throws an exception for unrecognized panel IDs. You must add your new panel here:
return match ($panel->getId()) { AdministrationPanelProvider::ID => $this->can(SystemPermission::AccessAdministrationPanel), PortalPanelProvider::ID => $this->can(SystemPermission::AccessPortal),};Add a new permission
Define a new permission in SystemPermission for your panel:
case AccessPortal = 'access-portal';Then add a description, set the system-managed flag, and configure the scope, following the patterns of the existing AccessAdministrationPanel permission.
Navigation Groups
The Administration panel organizes its sidebar with a HasLabel enum. Create an equivalent for your new panel:
namespace App\Filament\Portal\Navigation;
use Filament\Support\Contracts\HasLabel;
enum PortalNavGroup implements HasLabel{ case Dashboard; case MyRecords;
public function getLabel(): string { return match ($this) { self::Dashboard => 'Dashboard', self::MyRecords => 'My Records', }; }}Then reference it in your resources:
protected static string|null|UnitEnum $navigationGroup = PortalNavGroup::MyRecords;protected static ?int $navigationSort = 1;Generating Resources
When generating resources for your new panel, specify both the model namespace and the panel:
php artisan make:filament-resource Application \ --generate \ --model-namespace=App\\Domains\\Application\\Models \ --panel=portalThe --panel flag ensures the resource is placed in the correct directory for your panel’s discovery path.
Architecture Patterns to Follow
The Administration panel establishes several patterns worth replicating:
| Pattern | What to do | Reference |
|---|---|---|
| Panel ID constant | Define public const string ID on the provider | AdministrationPanelProvider::ID |
| Permission gating | One permission per panel in SystemPermission | AccessAdministrationPanel |
| Nav group enum | A HasLabel enum per panel for sidebar organization | AdministrationNavGroup |
| Heroicon constants | Use the Heroicon enum instead of raw strings | Heroicon::OutlinedUsers |
| Resource separation | Dedicated directories per panel under app/Filament/ | app/Filament/Resources/ |
| Policies | Standard Laravel policies (Filament respects them automatically) | UserPolicy, RolePolicy |