Skip to content

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.

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
  1. Generate the panel provider

    Terminal window
    php artisan make:filament-panel portal

    This creates a new provider at app/Providers/Filament/PortalPanelProvider.php and registers it automatically.

  2. Define a panel ID constant

    Follow the pattern from AdministrationPanelProvider. Define a public ID constant 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)
    // ...
    }
    }
  3. Configure the middleware stack

    Your new panel needs the same middleware foundation as the Administration panel. At minimum, include the InjectLivewireAssets middleware, 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,
    ])
  4. 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/*'),
    ])
  5. 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')
  6. 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(),
    ])

The User::canAccessPanel() method uses a match statement that intentionally throws an exception for unrecognized panel IDs. You must add your new panel here:

app/Domains/User/Models/User.php
return match ($panel->getId()) {
AdministrationPanelProvider::ID => $this->can(SystemPermission::AccessAdministrationPanel),
PortalPanelProvider::ID => $this->can(SystemPermission::AccessPortal),
};

Define a new permission in SystemPermission for your panel:

app/Domains/Auth/Enums/SystemPermission.php
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.

The Administration panel organizes its sidebar with a HasLabel enum. Create an equivalent for your new panel:

app/Filament/Portal/Navigation/PortalNavGroup.php
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;

When generating resources for your new panel, specify both the model namespace and the panel:

Terminal window
php artisan make:filament-resource Application \
--generate \
--model-namespace=App\\Domains\\Application\\Models \
--panel=portal

The --panel flag ensures the resource is placed in the correct directory for your panel’s discovery path.

The Administration panel establishes several patterns worth replicating:

PatternWhat to doReference
Panel ID constantDefine public const string ID on the providerAdministrationPanelProvider::ID
Permission gatingOne permission per panel in SystemPermissionAccessAdministrationPanel
Nav group enumA HasLabel enum per panel for sidebar organizationAdministrationNavGroup
Heroicon constantsUse the Heroicon enum instead of raw stringsHeroicon::OutlinedUsers
Resource separationDedicated directories per panel under app/Filament/app/Filament/Resources/
PoliciesStandard Laravel policies (Filament respects them automatically)UserPolicy, RolePolicy