filament/actions
Filament Actions adds reusable, customizable UI actions to Filament admin panels. Define buttons, modals, confirmations, and forms with a fluent API, then run callbacks, validations, and notifications consistently across tables, resources, and pages.
title: Modals
import Aside from "@components/Aside.astro" import AutoScreenshot from "@components/AutoScreenshot.astro" import UtilityInjection from "@components/UtilityInjection.astro"
Actions may require additional confirmation or input from the user before they run. You may open a modal before an action is executed to do this.
You may require confirmation before an action is run using the requiresConfirmation() method. This is useful for particularly destructive actions, such as those that delete records.
use App\Models\Post;
use Filament\Actions\Action;
Action::make('delete')
->action(fn (Post $record) => $record->delete())
->requiresConfirmation()
You may customize the heading, description and label of the submit button in the modal:
use App\Models\Post;
use Filament\Actions\Action;
Action::make('delete')
->action(fn (Post $record) => $record->delete())
->requiresConfirmation()
->modalHeading('Delete post')
->modalDescription('Are you sure you\'d like to delete this post? This cannot be undone.')
->modalSubmitActionLabel('Yes, delete it')
Filament allows you to render a schema in a modal, which allows you to render any of the available components to build a UI. Usually, it is useful to build a form in the schema that can collect extra information from the user before the action runs, but any UI can be rendered:
use Filament\Actions\Action;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Infolists\Components\TextEntry;
use Filament\Schemas\Components\Section;
Action::make('viewUser')
->schema([
Grid::make(2)
->schema([
Section::make('Details')
->schema([
TextInput::make('name'),
Select::make('position')
->options([
'developer' => 'Developer',
'designer' => 'Designer',
]),
Checkbox::make('is_admin'),
]),
Section::make('Auditing')
->schema([
TextEntry::make('created_at')
->dateTime(),
TextEntry::make('updated_at')
->dateTime(),
]),
]),
])
<UtilityInjection set="actions" version="5.x">As well as allowing a static value, the schema() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.</UtilityInjection>
You may use form field to create action modal forms. The data from the form is available in the $data array of the action() closure:
use App\Models\Post;
use App\Models\User;
use Filament\Actions\Action;
use Filament\Forms\Components\Select;
Action::make('updateAuthor')
->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();
})
You may fill the form with existing data, using the fillForm() method:
use App\Models\Post;
use App\Models\User;
use Filament\Actions\Action;
use Filament\Forms\Components\Select;
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();
})
<UtilityInjection set="actions" version="5.x">The fillForm() method also accepts a function to dynamically calculate the data to fill the form with. You can inject various utilities into the function as parameters.</UtilityInjection>
You may wish to disable all form fields in the modal, ensuring the user cannot edit them. You may do so using the disabledForm() method:
use App\Models\Post;
use Filament\Actions\Action;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
Action::make('approvePost')
->schema([
TextInput::make('title'),
Textarea::make('content'),
])
->disabledForm()
->action(function (Post $record): void {
$record->approve();
})
You may create a multistep form wizard inside a modal. Instead of using a schema(), define a steps() array and pass your Step objects:
use Filament\Actions\Action;
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()
->live()
->afterStateUpdated(fn ($state, callable $set) => $set('slug', Str::slug($state))),
TextInput::make('slug')
->disabled()
->required()
->unique(Category::class, 'slug'),
])
->columns(2),
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),
]),
])
You may add an icon inside the modal using the modalIcon() method:
use App\Models\Post;
use Filament\Actions\Action;
Action::make('delete')
->action(fn (Post $record) => $record->delete())
->requiresConfirmation()
->modalIcon('heroicon-o-trash')
<UtilityInjection set="actions" version="5.x">The modalIcon() method also accepts a function to dynamically calculate the value. You can inject various utilities into the function as parameters.</UtilityInjection>
By default, the icon will inherit the color of the action button. You may customize the color of the icon using the modalIconColor() method:
use App\Models\Post;
use Filament\Actions\Action;
Action::make('delete')
->action(fn (Post $record) => $record->delete())
->requiresConfirmation()
->color('danger')
->modalIcon('heroicon-o-trash')
->modalIconColor('warning')
<UtilityInjection set="actions" version="5.x">The modalIconColor() method also accepts a function to dynamically calculate the value. You can inject various utilities into the function as parameters.</UtilityInjection>
By default, modal content will be aligned to the start, or centered if the modal is xs or sm in width. If you wish to change the alignment of content in a modal, you can use the modalAlignment() method and pass it Alignment::Start or Alignment::Center:
use Filament\Actions\Action;
use Filament\Support\Enums\Alignment;
Action::make('updateAuthor')
->schema([
// ...
])
->action(function (array $data): void {
// ...
})
->modalAlignment(Alignment::Center)
<UtilityInjection set="actions" version="5.x">The modalAlignment() method also accepts a function to dynamically calculate the value. You can inject various utilities into the function as parameters.</UtilityInjection>
The header of a modal scrolls out of view with the modal content when it overflows the modal size. However, slide-overs have a sticky header that's always visible. You may control this behavior using stickyModalHeader():
use Filament\Actions\Action;
Action::make('updateAuthor')
->schema([
// ...
])
->action(function (array $data): void {
// ...
})
->stickyModalHeader()
The footer of a modal is rendered inline after the content by default. Slide-overs, however, have a sticky footer that always shows when scrolling the content. You may enable this for a modal too using stickyModalFooter():
use Filament\Actions\Action;
Action::make('updateAuthor')
->schema([
// ...
])
->action(function (array $data): void {
// ...
})
->stickyModalFooter()
You may define custom content to be rendered inside your modal, which you can specify by passing a Blade view into the modalContent() method:
use App\Models\Post;
use Filament\Actions\Action;
Action::make('advance')
->action(fn (Post $record) => $record->advance())
->modalContent(view('filament.pages.actions.advance'))
<UtilityInjection set="actions" version="5.x">The modalContent() method also accepts a function to dynamically calculate the value. You can inject various utilities into the function as parameters.</UtilityInjection>
You can pass data to the view by returning it from a function. For example, if the $record of an action is set, you can pass that through to the view:
use Filament\Actions\Action;
use Illuminate\Contracts\View\View;
Action::make('advance')
->action(fn (Contract $record) => $record->advance())
->modalContent(fn (Contract $record): View => view(
'filament.pages.actions.advance',
['record' => $record],
))
By default, the custom content is displayed above the modal form if there is one, but you can add content below using modalContentFooter() if you wish:
use App\Models\Post;
use Filament\Actions\Action;
Action::make('advance')
->action(fn (Post $record) => $record->advance())
->modalContentFooter(view('filament.pages.actions.advance'))
<UtilityInjection set="actions" version="5.x">The modalContentFooter() method also accepts a function to dynamically calculate the value. You can inject various utilities into the function as parameters.</UtilityInjection>
You can add an action button to your custom modal content, which is useful if you want to add a button that performs an action other than the main action. You can do this by registering an action with the registerModalActions() method, and then passing it to the view:
use App\Models\Post;
use Filament\Actions\Action;
use Illuminate\Contracts\View\View;
Action::make('advance')
->registerModalActions([
Action::make('report')
->requiresConfirmation()
->action(fn (Post $record) => $record->report()),
])
->action(fn (Post $record) => $record->advance())
->modalContent(fn (Action $action): View => view(
'filament.pages.actions.advance',
['action' => $action],
))
Now, in the view file, you can render the action button by calling getModalAction():
<div>
{{ $action->getModalAction('report') }}
</div>
You can open a "slide-over" dialog instead of a modal by using the slideOver() method:
use Filament\Actions\Action;
Action::make('updateAuthor')
->schema([
// ...
])
->action(function (array $data): void {
// ...
})
->slideOver()
Instead of opening in the center of the screen, the modal content will now slide in from the right and consume the entire height of the browser.
You can change the width of the modal by using the modalWidth() method. Options correspond to Tailwind's max-width scale. The options are ExtraSmall, Small, Medium, Large, ExtraLarge, TwoExtraLarge, ThreeExtraLarge, FourExtraLarge, FiveExtraLarge, SixExtraLarge, SevenExtraLarge, and Screen:
use Filament\Actions\Action;
use Filament\Support\Enums\Width;
Action::make('updateAuthor')
->schema([
// ...
])
->action(function (array $data): void {
// ...
})
->modalWidth(Width::FiveExtraLarge)
<UtilityInjection set="actions" version="5.x">The modalWidth() method also accepts a function to dynamically calculate the value. You can inject various utilities into the function as parameters.</UtilityInjection>
You may execute code within a closure when the modal opens, by passing it to the mountUsing() method:
use Filament\Actions\Action;
use Filament\Schemas\Schema;
Action::make('create')
->mountUsing(function (Schema $form) {
$form->fill();
// ...
})
The
mountUsing()method, by default, is used by Filament to initialize the form. If you override this method, you will need to call$form->fill()to ensure the form is initialized correctly. If you wish to populate the form with data, you can do so by passing an array to thefill()method, instead of usingfillForm()on the action itself.
By default, there are two actions in the footer of a modal. The first is a button to submit, which executes the action(). The second button closes the modal and cancels the action.
To modify the action instance that is used to render one of the default action buttons, you may pass a closure to the modalSubmitAction() and modalCancelAction() methods:
use Filament\Actions\Action;
Action::make('help')
->modalContent(view('actions.help'))
->modalCancelAction(fn (Action $action) => $action->label('Close'))
The methods available to customize trigger buttons will work to modify the $action instance inside the closure.
To remove a default action, you may pass false to either modalSubmitAction() or modalCancelAction():
use Filament\Actions\Action;
Action::make('help')
->modalContent(view('actions.help'))
->modalSubmitAction(false)
You may pass an array of extra actions to be rendered, between the default actions, in the footer of the modal using the extraModalFooterActions() method:
use Filament\Actions\Action;
Action::make('create')
->schema([
// ...
])
// ...
->extraModalFooterActions(fn (Action $action): array => [
$action->makeModalSubmitAction('createAnother', arguments: ['another' => true]),
])
<UtilityInjection set="actions" version="5.x">The extraModalFooterActions() method also accepts a function to dynamically calculate the value. You can inject various utilities into the function as parameters.</UtilityInjection>
$action->makeModalSubmitAction() returns an action instance that can be customized using the methods available to customize trigger buttons.
The second parameter of makeModalSubmitAction() allows you to pass an array of arguments that will be accessible inside the action's action() closure as $arguments. These could be useful as flags to indicate that the action should behave differently based on the user's decision:
use Filament\Actions\Action;
Action::make('create')
->schema([
// ...
])
// ...
->extraModalFooterActions(fn (Action $action): array => [
$action->makeModalSubmitAction('createAnother', arguments: ['another' => true]),
])
->action(function (array $data, array $arguments): void {
// Create
if ($arguments['another'] ?? false) {
// Reset the form and don't close the modal
}
})
You can nest actions within each other, allowing you to open a new modal from an extra footer action:
use Filament\Actions\Action;
Action::make('edit')
// ...
->extraModalFooterActions([
Action::make('delete')
->requiresConfirmation()
->action(function () {
// ...
}),
])
Now, the edit modal will have a "Delete" button in the footer, which will open a confirmation modal when clicked. This action is completely independent of the edit action, and will not run the edit action when it is clicked.
In this example though, you probably want to cancel the edit action if the delete action is run. You can do this using the cancelParentActions() method:
use Filament\Actions\Action;
Action::make('delete')
->requiresConfirmation()
->action(function () {
// ...
})
->cancelParentActions()
If you have deep nesting with multiple parent actions, but you don't want to cancel all of them, you can pass the name of the parent action you want to cancel, including its children, to cancelParentActions():
use Filament\Actions\Action;
Action::make('first')
->requiresConfirmation()
->action(function () {
// ...
})
->extraModalFooterActions([
Action::make('second')
->requiresConfirmation()
->action(function () {
// ...
})
->extraModalFooterActions([
Action::make('third')
->requiresConfirmation()
->action(function () {
// ...
})
->extraModalFooterActions([
Action::make('fourth')
->requiresConfirmation()
->action(function () {
// ...
})
->cancelParentActions('second'),
]),
]),
])
In this example, if the fourth action is run, the second action is canceled, but so is the third action since it is a child of second. The first action is not canceled, however, since it is the parent of second. The first action's modal will remain open.
You can access the instances of parent actions and their raw data and arguments by injecting the $mountedActions array in a function used by your nested action. For example, to get the top-most parent action currently active on the page, you can use $mountedActions[0]. From there, you can get the raw data for that action by calling $mountedActions[0]->getRawData(). Please be aware that raw data is not validated since the action has not been submitted yet:
use Filament\Actions\Action;
use Filament\Forms\Components\TextInput;
Action::make('first')
->schema([
TextInput::make('foo'),
])
->action(function () {
// ...
})
->extraModalFooterActions([
Action::make('second')
->requiresConfirmation()
->action(function (array $mountedActions) {
dd($mountedActions[0]->getRawData());
// ...
}),
])
You can...
How can I help you explore Laravel packages today?