SYS://VISION.ACTIVE
VIEWPORT.01
LAT 28.0222° N
SIGNAL.NOMINAL
VISION Loading
Back to Blog

Building Admin Panels with Filament: From Setup to Production

Shane Barron

Shane Barron

Laravel Developer & AI Integration Specialist

Why Filament?

Filament has revolutionized admin panel development in Laravel. Where we once spent weeks building CRUD interfaces, Filament delivers production-ready admin panels in hours. It's built on Livewire and Alpine.js, providing a reactive experience without writing JavaScript.

Setting Up Filament

Installation is straightforward:

composer require filament/filament
php artisan filament:install --panels
php artisan make:filament-user

Creating Resources

Resources are the heart of Filament—they define how models are displayed, created, and edited:

php artisan make:filament-resource Order --generate

This generates a resource with form and table schemas based on your model's fillable attributes. From there, customize to your needs:

class OrderResource extends Resource
{
    protected static ?string $model = Order::class;
    protected static ?string $navigationIcon = 'heroicon-o-shopping-cart';
    protected static ?string $navigationGroup = 'Sales';

    public static function form(Form $form): Form
    {
        return $form->schema([
            Forms\Components\Section::make('Order Details')->schema([
                Forms\Components\Select::make('customer_id')
                    ->relationship('customer', 'name')
                    ->searchable()
                    ->preload()
                    ->required(),
                Forms\Components\Select::make('status')
                    ->options([
                        'pending' => 'Pending',
                        'processing' => 'Processing',
                        'shipped' => 'Shipped',
                        'delivered' => 'Delivered',
                    ])
                    ->required(),
                Forms\Components\DatePicker::make('order_date')
                    ->default(now())
                    ->required(),
            ])->columns(3),

            Forms\Components\Section::make('Items')->schema([
                Forms\Components\Repeater::make('items')
                    ->relationship()
                    ->schema([
                        Forms\Components\Select::make('product_id')
                            ->relationship('product', 'name')
                            ->searchable()
                            ->required()
                            ->reactive()
                            ->afterStateUpdated(fn ($state, callable $set) =>
                                $set('unit_price', Product::find($state)?->price ?? 0)
                            ),
                        Forms\Components\TextInput::make('quantity')
                            ->numeric()
                            ->default(1)
                            ->required(),
                        Forms\Components\TextInput::make('unit_price')
                            ->numeric()
                            ->prefix('$')
                            ->disabled(),
                    ])->columns(3),
            ]),
        ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                Tables\Columns\TextColumn::make('id')
                    ->sortable(),
                Tables\Columns\TextColumn::make('customer.name')
                    ->searchable(),
                Tables\Columns\BadgeColumn::make('status')
                    ->colors([
                        'warning' => 'pending',
                        'primary' => 'processing',
                        'success' => fn ($state) => in_array($state, ['shipped', 'delivered']),
                    ]),
                Tables\Columns\TextColumn::make('total')
                    ->money('USD')
                    ->sortable(),
                Tables\Columns\TextColumn::make('created_at')
                    ->dateTime()
                    ->sortable(),
            ])
            ->filters([
                Tables\Filters\SelectFilter::make('status')
                    ->options([
                        'pending' => 'Pending',
                        'processing' => 'Processing',
                        'shipped' => 'Shipped',
                        'delivered' => 'Delivered',
                    ]),
                Tables\Filters\Filter::make('high_value')
                    ->query(fn ($query) => $query->where('total', '>', 1000)),
            ])
            ->actions([
                Tables\Actions\EditAction::make(),
                Tables\Actions\Action::make('ship')
                    ->action(fn (Order $record) => $record->update(['status' => 'shipped']))
                    ->requiresConfirmation()
                    ->visible(fn (Order $record) => $record->status === 'processing'),
            ]);
    }
}

Custom Pages and Widgets

Filament makes it easy to add dashboard widgets:

class SalesOverview extends Widget
{
    protected function getCards(): array
    {
        return [
            Card::make('Total Revenue', '$' . number_format(Order::sum('total'), 2)),
            Card::make('Orders This Month', Order::whereMonth('created_at', now())->count()),
            Card::make('Average Order Value', '$' . number_format(Order::avg('total'), 2)),
        ];
    }
}

Authorization

Integrate with Laravel's policies for fine-grained access control:

class OrderPolicy
{
    public function viewAny(User $user): bool
    {
        return $user->hasPermission('orders.view');
    }

    public function update(User $user, Order $order): bool
    {
        return $user->hasPermission('orders.edit')
            && ($user->isAdmin() || $order->assigned_to === $user->id);
    }
}

Conclusion

Filament transforms admin panel development from a chore into a pleasure. Its declarative approach, combined with Livewire's reactivity, delivers powerful interfaces with minimal code. Start with the generated resources and customize from there—you'll be amazed at what you can build in an afternoon.

Share this article
Shane Barron

Shane Barron

Strategic Technology Architect with 40 years of experience building production systems. Specializing in Laravel, AI integration, and enterprise architecture.

Need Help With Your Project?

I respond to all inquiries within 24 hours. Let's discuss how I can help build your production-ready system.

Get In Touch