Skip to main content
1

Create your Laravel project

If you haven’t already, create a new Laravel project:
composer create-project laravel/laravel blog
cd blog
Then, install Filament following the panel builder installation instructions.
2

Create models and migrations

Let’s build a simple blog with Authors and Posts. Create the models:
php artisan make:model Author -m
php artisan make:model Post -m
Update the migrations in database/migrations/:
// create_authors_table migration
Schema::create('authors', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email')->unique();
    $table->text('bio')->nullable();
    $table->timestamps();
});

// create_posts_table migration
Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('author_id')->constrained()->cascadeOnDelete();
    $table->string('title');
    $table->string('slug')->unique();
    $table->text('content');
    $table->boolean('is_published')->default(false);
    $table->timestamp('published_at')->nullable();
    $table->timestamps();
});
Run the migrations:
php artisan migrate
3

Define relationships

Add the relationship methods to your models:
// app/Models/Author.php
public function posts(): HasMany
{
    return $this->hasMany(Post::class);
}

// app/Models/Post.php
public function author(): BelongsTo
{
    return $this->belongsTo(Author::class);
}
4

Generate Filament resources

Use the --generate flag to automatically create resources with forms and tables:
php artisan make:filament-resource Author --generate
php artisan make:filament-resource Post --generate
This creates AuthorResource.php and PostResource.php in app/Filament/Resources/ with automatically generated forms, tables, and pages.
5

Customize the forms

Open app/Filament/Resources/PostResource.php and customize the form:
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\MarkdownEditor;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;

public static function form(Form $form): Form
{
    return $form
        ->schema([
            TextInput::make('title')
                ->required()
                ->maxLength(255)
                ->live(onBlur: true)
                ->afterStateUpdated(fn (Set $set, ?string $state) => $set('slug', Str::slug($state))),
            
            TextInput::make('slug')
                ->required()
                ->maxLength(255)
                ->unique(ignoreRecord: true),
            
            Select::make('author_id')
                ->relationship('author', 'name')
                ->required()
                ->searchable()
                ->preload(),
            
            MarkdownEditor::make('content')
                ->required()
                ->columnSpanFull(),
            
            Toggle::make('is_published')
                ->default(false),
            
            DateTimePicker::make('published_at')
                ->label('Publish Date'),
        ]);
}
6

Enhance the table

Update the table configuration to add filters and improve the display:
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\Filter;
use Filament\Tables\Filters\SelectFilter;
use Illuminate\Database\Eloquent\Builder;

public static function table(Table $table): Table
{
    return $table
        ->columns([
            TextColumn::make('title')
                ->searchable()
                ->sortable(),
            
            TextColumn::make('author.name')
                ->searchable()
                ->sortable(),
            
            IconColumn::make('is_published')
                ->boolean(),
            
            TextColumn::make('published_at')
                ->dateTime()
                ->sortable(),
            
            TextColumn::make('created_at')
                ->dateTime()
                ->sortable()
                ->toggleable(isToggledHiddenByDefault: true),
        ])
        ->filters([
            SelectFilter::make('author')
                ->relationship('author', 'name'),
            
            Filter::make('is_published')
                ->query(fn (Builder $query): Builder => $query->where('is_published', true)),
        ])
        ->actions([
            Tables\Actions\EditAction::make(),
            Tables\Actions\DeleteAction::make(),
        ])
        ->bulkActions([
            Tables\Actions\BulkActionGroup::make([
                Tables\Actions\DeleteBulkAction::make(),
            ]),
        ]);
}
7

Configure the resources

Add navigation icons and grouping to your resources:
// In PostResource.php
protected static ?string $navigationIcon = 'heroicon-o-document-text';
protected static ?string $navigationGroup = 'Blog';
protected static ?int $navigationSort = 1;

// In AuthorResource.php
protected static ?string $navigationIcon = 'heroicon-o-user-group';
protected static ?string $navigationGroup = 'Blog';
protected static ?int $navigationSort = 2;
8

Test your admin panel

Create a user if you haven’t already:
php artisan make:filament-user
Then visit /admin in your browser, log in, and start managing your blog!

What you’ve built

Author Management

Full CRUD interface for managing blog authors with searchable tables and validated forms.

Post Management

Create and edit blog posts with markdown content, auto-generated slugs, and publishing controls.

Relationships

Select authors for posts with searchable dropdowns and see author details in the posts table.

Filters & Search

Filter posts by author and publication status, plus full-text search on titles and authors.

Next steps

Now that you have a working admin panel, explore more features:

Common commands

php artisan make:filament-resource ModelName
php artisan make:filament-resource ModelName --generate
php artisan make:filament-resource ModelName --simple
php artisan make:filament-resource ModelName --view

Build docs developers (and LLMs) love