Cover image for Building Powerful Admin Panels with Laravel 12 and Filament v5: A Production Guide

At a glance

Reading time

~200 words/min

Published

3 weeks ago

May 17, 2026

Views

443

All-time total

Building Powerful Admin Panels with Laravel 12 and Filament v5: A Production Guide

Filament v5 is the version that finally makes "build the admin panel" a one-evening task instead of a two-week project. Released alongside Laravel 12, it ships with rebuilt forms and tables, native multi-tenancy, and a plugin ecosystem that genuinely covers the long tail. This guide is what I wish someone had handed me on day one — the patterns that scale to ten resources, fifty, and beyond.

What this guide covers

  • Installing Filament v5 cleanly on a fresh Laravel 12 app
  • Designing your first Resource with relations, filters, and bulk actions
  • Role-based access with Spatie Permissions
  • Multi-tenancy: tenant-scoped resources without query leaks
  • Theming, custom widgets, and a deployment checklist
i

Info

Filament v5 in one sentence

A Livewire-powered admin panel framework that gives you tables, forms, and dashboards in a few PHP classes — no separate frontend, no API, no JSON contract to maintain.

1. Installation that actually works on day 30

composer require filament/filament:"^5.0" -W
php artisan filament:install --panels
php artisan make:filament-user

The default panel lives at /admin. Before you ship anything, lock it down.

// app/Providers/Filament/AdminPanelProvider.php
public function panel(Panel $panel): Panel
{
    return $panel
        ->default()
        ->id('admin')
        ->path('admin')
        ->login()
        ->authGuard('web')
        ->colors(['primary' => Color::Teal])
        ->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
        ->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
        ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
        ->middleware([
            EncryptCookies::class,
            AddQueuedCookiesToResponse::class,
            StartSession::class,
            AuthenticateSession::class,
            ShareErrorsFromSession::class,
            VerifyCsrfToken::class,
            SubstituteBindings::class,
            DisableBladeIconComponents::class,
            DispatchServingFilamentEvent::class,
        ])
        ->authMiddleware([Authenticate::class, EnsureUserCanAccessFilament::class]);
}

Warning

Default access policy is too permissive

Filament out of the box lets any authenticated user reach <code>/admin</code>. Add an <code>EnsureUserCanAccessFilament</code> middleware that checks <code>$user-&gt;is_admin</code> or a Spatie role. Without it, your customer support team logs in to the admin panel by accident.

2. Your first Resource — done right

php artisan make:filament-resource Project --generate
// app/Filament/Resources/ProjectResource.php
public static function form(Form $form): Form
{
    return $form->schema([
        Forms\Components\Section::make('Project details')
            ->columns(2)
            ->schema([
                Forms\Components\TextInput::make('name')
                    ->required()
                    ->maxLength(120)
                    ->live(onBlur: true)
                    ->afterStateUpdated(fn ($set, $state) => $set('slug', Str::slug($state))),

                Forms\Components\TextInput::make('slug')
                    ->required()
                    ->unique(ignoreRecord: true),

                Forms\Components\Select::make('owner_id')
                    ->relationship('owner', 'name')
                    ->searchable()
                    ->preload()
                    ->required(),

                Forms\Components\Select::make('status')
                    ->options(['draft' => 'Draft', 'active' => 'Active', 'archived' => 'Archived'])
                    ->default('draft')
                    ->required(),

                Forms\Components\RichEditor::make('description')
                    ->columnSpanFull()
                    ->disableToolbarButtons(['attachFiles']),
            ]),
    ]);
}

public static function table(Table $table): Table
{
    return $table
        ->columns([
            Tables\Columns\TextColumn::make('name')->searchable()->sortable(),
            Tables\Columns\TextColumn::make('owner.name')->label('Owner')->sortable(),
            Tables\Columns\BadgeColumn::make('status')
                ->colors(['gray' => 'draft', 'success' => 'active', 'warning' => 'archived']),
            Tables\Columns\TextColumn::make('updated_at')->since()->sortable(),
        ])
        ->filters([
            Tables\Filters\SelectFilter::make('status')->options(['draft', 'active', 'archived']),
            Tables\Filters\Filter::make('mine')
                ->toggle()
                ->query(fn ($query) => $query->where('owner_id', auth()->id())),
        ])
        ->actions([Tables\Actions\EditAction::make(), Tables\Actions\DeleteAction::make()])
        ->bulkActions([Tables\Actions\DeleteBulkAction::make()])
        ->defaultSort('updated_at', 'desc');
}
💡

Pro tip

Always pass <code>ignoreRecord: true</code> on unique validators inside Resources — without it, editing a record fails because Filament re-validates the existing slug against itself.

3. Role-based access with Spatie Permissions

composer require spatie/laravel-permission
// app/Filament/Resources/ProjectResource.php
public static function canViewAny(): bool   { return auth()->user()->can('view-any project'); }
public static function canCreate(): bool    { return auth()->user()->can('create project'); }
public static function canEdit($record): bool   { return auth()->user()->can('update', $record); }
public static function canDelete($record): bool { return auth()->user()->can('delete', $record); }

Wire the same permissions into your model policies and you get one source of truth — Filament inherits the policy automatically when you implement canEdit via the Gate.

4. Multi-tenancy without leaks

Filament v5 has first-class tenancy. Tell the panel which model represents a tenant and every resource is scoped automatically.

// In AdminPanelProvider::panel()
->tenant(Workspace::class, slugAttribute: 'slug')
->tenantRegistration(RegisterWorkspace::class)
->tenantProfile(EditWorkspaceProfile::class)
// app/Models/Workspace.php
public function getFilamentName(): string { return $this->name; }
public function canAccessFilament(): bool { return $this->users->contains(auth()->user()); }

// app/Models/User.php
public function getTenants(Panel $panel): Collection
{
    return $this->workspaces;
}

public function canAccessTenant(Model $tenant): bool
{
    return $this->workspaces->contains($tenant);
}

Danger

Tenant scoping is opt-in per resource

Resources that have a direct relationship to the tenant model auto-scope. Resources that go through pivot tables or polymorphic relations need explicit query overrides. Audit every Resource: open the table, switch tenants, confirm zero rows leak. Do this <em>before</em> any customer sees the panel.

5. Custom widgets that earn their keep

// app/Filament/Widgets/ProjectsOverview.php
class ProjectsOverview extends BaseWidget
{
    protected function getStats(): array
    {
        $tenant = Filament::getTenant();

        return [
            Stat::make('Active projects', $tenant->projects()->where('status', 'active')->count())
                ->description('Last 30 days')
                ->descriptionIcon('heroicon-o-arrow-trending-up')
                ->color('success'),

            Stat::make('Open tasks', $tenant->tasks()->whereNull('completed_at')->count())
                ->color('warning'),

            Stat::make('Avg. response time', '4h 12m')
                ->color('primary'),
        ];
    }
}
60%

less time spent on CRUD vs hand-rolling Blade + Tailwind from scratch

Pros

  • Resources cover 80% of admin needs out of the box
  • First-class multi-tenancy and RBAC
  • Plugin ecosystem (Shield for permissions, Spatie media library, etc.)
  • Looks polished without designer involvement

Cons

  • Customising deeply means writing Livewire — fight or learn the framework
  • Bundle size is non-trivial; ship Filament behind auth, never on public pages
  • Octane compatibility has rough edges; test before enabling

Production checklist

1

Lock the panel behind a middleware

Even on staging. The number of leaked admin panels indexed by Google is depressing.

2

Cache permissions

<code>spatie.permission.cache</code> on Redis. Without it, every page load hits the permissions table.

3

Run `php artisan filament:optimize` on deploy

It precompiles Filament assets — saves roughly 200ms on cold cache.

4

Set up tenant tests

A single Pest test that creates two tenants and asserts zero cross-leak prevents the worst class of bugs.

Success

You will reach for Filament more than you expect

Once a team ships their first panel they tend to consolidate every internal tool into Filament — settings, billing review, customer support, audit logs. That is a feature, not a bug.

Newsletter

Want more posts like this?

Get practical software notes and tutorials delivered when something new is published.

No spam. Unsubscribe anytime.

How did this land?

Comments

0
Log in or sign up to join the discussion and react to this post.

No comments yet. Be the first to share your thoughts.

Related posts

Essential Sorting Algorithms for Computer Science Students

Algorithms are commonly taught in Computer Science, Software Engineering subjects at your Bachelors or Masters. Some find it difficult to understand due to memorizing.

6 years ago

GraphQL in Laravel Using Lighthouse

In modern web development, GraphQL has emerged as a powerful alternative to REST APIs due to its flexibility and efficiency.

1 year ago

Building Modern Reactive UIs with Laravel 12 and Livewire 4: A Production Guide

A production-grade walkthrough of Livewire 4 in Laravel 12 — form objects, lazy components, Alpine interop, file uploads, Pest tests, and the deployment gotchas nobody warns you about.

1 day ago

Scaling Laravel 12 with Octane and FrankenPHP: A Production Performance Guide

Cut Laravel 12 latency by more than half with Octane and FrankenPHP — install, configure, audit singletons, and benchmark, with the production gotchas that bite teams in week two.

3 weeks ago

Multi-Tenant SaaS with Laravel 12: A Production Architecture Guide

A practical, opinionated architecture for multi-tenant SaaS on Laravel 12 — schema, subdomain routing, tenant-aware queues, Cashier billing, and the leak tests that keep you out of the news.

1 week ago