Skip to main content

Introduction

Actions can open modals before executing, allowing you to collect additional information from users or require confirmation for important operations. Modals can contain forms, wizards, custom content, and action buttons.

Confirmation modals

The simplest modal is a confirmation dialog that asks users to verify their intent before executing a destructive action:
use Filament\Actions\Action;

Action::make('delete')
    ->requiresConfirmation()
    ->action(fn () => $this->post->delete())
Confirmation modals are not available when using url() instead of action(). If you need to redirect to a URL after confirmation, do so within the action() closure.

Customizing confirmation text

You can customize the modal’s heading, description, and submit button label:
Action::make('delete')
    ->requiresConfirmation()
    ->modalHeading('Delete post')
    ->modalDescription('Are you sure you\'d like to delete this post? This cannot be undone.')
    ->modalSubmitActionLabel('Yes, delete it')
    ->action(fn () => $this->post->delete())

Form modals

You can render a form inside a modal to collect data from users before the action executes:
1
Define the form schema
2
Use the schema() method to add form fields:
3
use Filament\Actions\Action;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;

Action::make('updateAuthor')
    ->schema([
        Select::make('authorId')
            ->label('Author')
            ->options(User::query()->pluck('name', 'id'))
            ->required(),
        TextInput::make('reason')
            ->label('Reason for change')
            ->required(),
    ])
    ->action(function (array $data, Post $record): void {
        $record->author()->associate($data['authorId']);
        $record->update(['author_change_reason' => $data['reason']]);
    })
4
Access form data
5
The submitted form data is available as the $data parameter:
6
->action(function (array $data) {
    // $data['authorId'] contains the selected author ID
    // $data['reason'] contains the reason text
})
7
Fill the form with existing data
8
Pre-populate form fields using fillForm():
9
Action::make('updateAuthor')
    ->fillForm(fn (Post $record): array => [
        'authorId' => $record->author->id,
    ])
    ->schema([
        Select::make('authorId')
            ->label('Author')
            ->options(User::query()->pluck('name', 'id'))
            ->required(),
    ])
    ->action(function (array $data, Post $record): void {
        $record->author()->associate($data['authorId']);
        $record->save();
    })

Disabling form fields

Make all form fields read-only using disabledForm():
Action::make('approvePost')
    ->schema([
        TextInput::make('title'),
        Textarea::make('content'),
    ])
    ->disabledForm()
    ->action(fn (Post $record) => $record->approve())

Wizard modals

Create multi-step workflows inside modals using wizards:
use Filament\Forms\Components\MarkdownEditor;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Wizard\Step;

Action::make('create')
    ->steps([
        Step::make('Name')
            ->description('Give the category a unique name')
            ->schema([
                TextInput::make('name')->required(),
                TextInput::make('slug')->required(),
            ]),
        Step::make('Description')
            ->description('Add some extra details')
            ->schema([
                MarkdownEditor::make('description'),
            ]),
        Step::make('Visibility')
            ->description('Control who can view it')
            ->schema([
                Toggle::make('is_visible')
                    ->label('Visible to customers')
                    ->default(true),
            ]),
    ])
    ->action(function (array $data) {
        Category::create($data);
    })

Allowing step navigation

By default, users must complete each step in order. Allow free navigation with skippableSteps():
Action::make('create')
    ->steps([...])
    ->skippableSteps()
1
Adding an icon
2
Display an icon at the top of the modal:
3
Action::make('delete')
    ->requiresConfirmation()
    ->modalIcon('heroicon-o-trash')
    ->modalIconColor('danger')
    ->action(fn () => $this->post->delete())
4
Changing the width
5
Adjust the modal width using Tailwind’s max-width scale:
6
use Filament\Support\Enums\Width;

Action::make('updateAuthor')
    ->schema([...])
    ->modalWidth(Width::FiveExtraLarge)
7
Using slide-overs
8
Open a slide-over panel instead of a centered modal:
9
Action::make('updateAuthor')
    ->slideOver()
    ->schema([...])
10
Customizing alignment
11
Change the content alignment within the modal:
12
use Filament\Support\Enums\Alignment;

Action::make('updateAuthor')
    ->modalAlignment(Alignment::Center)
    ->schema([...])

Custom modal content

You can render custom Blade views inside your modals:
Action::make('showDetails')
    ->modalContent(view('filament.modals.post-details'))
    ->modalContentFooter(view('filament.modals.post-footer'))

Passing data to views

Pass data to your custom views using a closure:
Action::make('showDetails')
    ->modalContent(fn (Post $record): View => view(
        'filament.modals.post-details',
        ['post' => $record],
    ))
1
Customizing default buttons
2
Modify the submit and cancel buttons:
3
Action::make('help')
    ->modalContent(view('actions.help'))
    ->modalSubmitActionLabel('Got it')
    ->modalCancelActionLabel('Close')
4
For more control, use a closure:
5
Action::make('help')
    ->modalCancelAction(fn (Action $action) => $action->label('Close')->color('gray'))
6
Removing default buttons
7
Remove the submit or cancel button:
8
Action::make('help')
    ->modalContent(view('actions.help'))
    ->modalSubmitAction(false)
10
Add additional buttons to the modal footer:
11
Action::make('create')
    ->schema([...])
    ->extraModalFooterActions(fn (Action $action): array => [
        $action->makeModalSubmitAction('createAnother', arguments: ['another' => true]),
    ])
    ->action(function (array $data, array $arguments): void {
        Category::create($data);
        
        if ($arguments['another'] ?? false) {
            // Reset form and keep modal open
        }
    })

Nested actions

You can open child action modals from parent actions:
Action::make('edit')
    ->schema([...])
    ->extraModalFooterActions([
        Action::make('delete')
            ->requiresConfirmation()
            ->action(fn () => $this->post->delete())
            ->cancelParentActions(),
    ])
Use cancelParentActions() to close parent modals when a child action completes. You can pass a specific parent action name to only cancel that action and its children.

Closing behavior

Control how users can close the modal:
Action::make('updateAuthor')
    ->closeModalByClickingAway(false)
Make the header and footer always visible:
Action::make('edit')
    ->slideOver()
    ->stickyModalHeader()
    ->stickyModalFooter()

Disabling autofocus

Prevent the modal from automatically focusing the first field:
Action::make('updateAuthor')
    ->modalAutofocus(false)

Advanced techniques

Conditionally hiding modals

Show or hide the modal based on conditions:
Action::make('create')
    ->modalHidden(fn () => $this->user->isAdmin())
    ->action(fn () => $this->createPost())

Executing code on modal open

Run code when the modal opens:
use Filament\Schemas\Schema;

Action::make('create')
    ->mountUsing(function (Schema $form) {
        $form->fill(['status' => 'draft']);
        
        // Additional setup code
    })

Optimizing performance

Tell Filament a modal exists to skip the existence check:
Action::make('updateAuthor')
    ->modal() // Skips the check
    ->schema([...])

Extra HTML attributes

Add custom attributes to the modal window:
Action::make('updateAuthor')
    ->extraModalWindowAttributes([
        'class' => 'update-author-modal',
        'data-modal-type' => 'form',
    ])

Build docs developers (and LLMs) love