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. However, many applications need more than just an admin area. 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 heavily custom interfaces. But for authenticated, data-driven experiences — especially those involving tables, forms, filters, and bulk actions — Filament’s component library can save significant 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 — any authenticated user 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 established by 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 — it 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:

    ->colors(['primary' => Color::Purple])
    ->favicon('https://common.northwestern.edu/v8/icons/favicon-32.png')
    ->brandLogo(config('northwestern-theme.lockup'))
    ->databaseNotifications()
    ->databaseNotificationsPolling('30s')
    ->globalSearch()
    ->globalSearchDebounce('500ms')
    ->plugins([
    EnvironmentIndicatorPlugin::make()
    ->visible(! app()->isProduction())
    ->color(Color::Yellow),
    ])

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 automaticallyUserPolicy, RolePolicy