Skip to main content

Introduction

Actions are interactive buttons that can be added to tables in various positions:
  • Record actions - Buttons at the end of each table row
  • Bulk actions - Operations on selected rows
  • Header actions - Buttons in the table header
  • Toolbar actions - Buttons in the table toolbar
  • Column actions - Clickable cells that trigger actions
Actions can open modals, request confirmation, collect form data, and execute custom logic.

Record actions

Add action buttons to the end of each table row:
use Filament\Actions\Action;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->recordActions([
            Action::make('edit')
                ->url(fn (Post $record): string => route('posts.edit', $record))
                ->openUrlInNewTab(),
            Action::make('delete')
                ->requiresConfirmation()
                ->action(fn (Post $record) => $record->delete()),
        ]);
}

Accessing the record

All action callbacks receive the current $record:
Action::make('feature')
    ->action(function (Post $record): void {
        $record->is_featured = true;
        $record->save();
    })
    ->hidden(fn (Post $record): bool => $record->is_featured)

Positioning record actions

Move actions before columns:
use Filament\Tables\Enums\RecordActionsPosition;

public function table(Table $table): Table
{
    return $table
        ->recordActions([
            // ...
        ], position: RecordActionsPosition::BeforeColumns);
}
Move actions before the checkbox column:
public function table(Table $table): Table
{
    return $table
        ->recordActions([
            // ...
        ], position: RecordActionsPosition::BeforeCells);
}

Action modals

Actions can open modals for confirmation or to collect additional data:
use Filament\Actions\Action;
use Filament\Forms\Components\Textarea;
use App\Models\Post;

Action::make('reject')
    ->requiresConfirmation()
    ->modalHeading('Reject post')
    ->modalDescription('Are you sure you want to reject this post?')
    ->modalSubmitActionLabel('Yes, reject it')
    ->action(fn (Post $record) => $record->update(['status' => 'rejected']))

Actions with forms

Collect data before executing the action:
Action::make('changeStatus')
    ->form([
        Select::make('status')
            ->options([
                'draft' => 'Draft',
                'reviewing' => 'Reviewing',
                'published' => 'Published',
            ])
            ->required(),
        Textarea::make('note')
            ->label('Note (optional)'),
    ])
    ->action(function (Post $record, array $data): void {
        $record->update([
            'status' => $data['status'],
            'status_note' => $data['note'],
        ]);
    })

Pre-filling form data

Action::make('edit')
    ->form([
        TextInput::make('title')->required(),
        Textarea::make('content')->required(),
    ])
    ->fillForm(fn (Post $record): array => [
        'title' => $record->title,
        'content' => $record->content,
    ])
    ->action(function (Post $record, array $data): void {
        $record->update($data);
    })

Bulk actions

Execute operations on multiple selected records:
use Filament\Actions\BulkAction;
use Illuminate\Database\Eloquent\Collection;

public function table(Table $table): Table
{
    return $table
        ->toolbarActions([
            BulkAction::make('delete')
                ->requiresConfirmation()
                ->action(fn (Collection $records) => $records->each->delete()),
        ]);
}

Accessing selected records

The $records parameter is an Eloquent collection:
BulkAction::make('markAsPublished')
    ->action(function (Collection $records): void {
        $records->each(function (Post $record): void {
            $record->update(['status' => 'published']);
        });
    })

Authorizing bulk actions

Check policy methods for each selected record:
BulkAction::make('delete')
    ->requiresConfirmation()
    ->authorizeIndividualRecords('delete')
    ->action(fn (Collection $records) => $records->each->delete())
Records that fail authorization will be excluded from $records.

Bulk action notifications

Provide feedback about partial successes:
BulkAction::make('delete')
    ->requiresConfirmation()
    ->authorizeIndividualRecords('delete')
    ->action(fn (Collection $records) => $records->each->delete())
    ->successNotificationTitle('Deleted users')
    ->failureNotificationTitle(function (int $successCount, int $totalCount): string {
        if ($successCount) {
            return "{$successCount} of {$totalCount} users deleted";
        }

        return 'Failed to delete any users';
    })

Grouping bulk actions

Organize multiple bulk actions in a dropdown:
use Filament\Actions\BulkAction;
use Filament\Actions\BulkActionGroup;

public function table(Table $table): Table
{
    return $table
        ->toolbarActions([
            BulkActionGroup::make([
                BulkAction::make('delete')
                    ->requiresConfirmation()
                    ->action(fn (Collection $records) => $records->each->delete()),
                BulkAction::make('forceDelete')
                    ->requiresConfirmation()
                    ->action(fn (Collection $records) => $records->each->forceDelete()),
            ]),
        ]);
}
Shorthand when all bulk actions are grouped:
public function table(Table $table): Table
{
    return $table
        ->groupedBulkActions([
            BulkAction::make('delete')
                ->requiresConfirmation()
                ->action(fn (Collection $records) => $records->each->delete()),
            BulkAction::make('forceDelete')
                ->requiresConfirmation()
                ->action(fn (Collection $records) => $records->each->forceDelete()),
        ]);
}

Deselecting after bulk action

BulkAction::make('delete')
    ->action(fn (Collection $records) => $records->each->delete())
    ->deselectRecordsAfterCompletion()

Performance optimization

Process large selections in chunks:
use Illuminate\Support\LazyCollection;

BulkAction::make('delete')
    ->chunkSelectedRecords(250)
    ->action(function (LazyCollection $records) {
        $records->each->delete();
    })
Or work with IDs only:
use Illuminate\Support\Collection;

BulkAction::make('delete')
    ->fetchSelectedRecords(false)
    ->action(function (Collection $recordIds) {
        Post::whereIn('id', $recordIds)->delete();
    })

Limiting record selection

Restrict which records can be selected:
use Illuminate\Database\Eloquent\Model;

public function table(Table $table): Table
{
    return $table
        ->checkIfRecordIsSelectableUsing(
            fn (Model $record): bool => $record->status === Status::Enabled,
        );
}
Limit total number of selections:
public function table(Table $table): Table
{
    return $table
        ->maxSelectableRecords(4);
}
Prevent selecting all pages at once:
public function table(Table $table): Table
{
    return $table
        ->selectCurrentPageOnly();
}

Header actions

Add actions to the table header:
use Filament\Actions\Action;

public function table(Table $table): Table
{
    return $table
        ->headerActions([
            Action::make('create')
                ->url(route('posts.create'))
                ->icon('heroicon-o-plus'),
        ]);
}
Header actions can be regular actions or bulk actions.

Toolbar actions

Add actions to the table toolbar:
public function table(Table $table): Table
{
    return $table
        ->toolbarActions([
            Action::make('export')
                ->action(fn () => $this->export())
                ->icon('heroicon-o-arrow-down-tray'),
        ]);
}

Column actions

Make column cells clickable to trigger actions:
use Filament\Tables\Columns\TextColumn;
use Filament\Actions\Action;

TextColumn::make('title')
    ->action(
        Action::make('viewPost')
            ->url(fn (Post $record): string => route('posts.show', $record))
    )
Simple click callback:
TextColumn::make('title')
    ->action(function (Post $record): void {
        $this->dispatch('open-post-edit-modal', post: $record->getKey());
    })

Grouping actions

Group multiple actions in a dropdown:
use Filament\Actions\ActionGroup;
use Filament\Actions\DeleteAction;
use Filament\Actions\EditAction;
use Filament\Actions\ViewAction;

public function table(Table $table): Table
{
    return $table
        ->recordActions([
            ActionGroup::make([
                ViewAction::make(),
                EditAction::make(),
                DeleteAction::make(),
            ]),
        ]);
}
ActionGroup::make([
    ViewAction::make(),
    EditAction::make(),
    DeleteAction::make(),
])
    ->label('More actions')
    ->icon('heroicon-m-ellipsis-vertical')
    ->size('sm')
    ->color('primary')
    ->button()

Action customization

Labels and icons

Action::make('edit')
    ->label('Edit post')
    ->icon('heroicon-o-pencil')
    ->iconPosition('after')

Colors

Action::make('delete')
    ->color('danger')

Action::make('publish')
    ->color('success')

Sizes

Action::make('edit')
    ->size('sm') // xs, sm, md, lg, xl

Button styles

Action::make('edit')
    ->button() // Solid button

Action::make('edit')
    ->iconButton() // Icon only

Action::make('edit')
    ->link() // Text link

Conditional visibility

Action::make('publish')
    ->visible(fn (Post $record): bool => $record->status === 'draft')

Action::make('unpublish')
    ->hidden(fn (Post $record): bool => $record->status === 'draft')

Disabled state

Action::make('delete')
    ->disabled(fn (Post $record): bool => $record->is_locked)

Notifications

Send notifications after actions:
use Filament\Notifications\Notification;

Action::make('publish')
    ->action(function (Post $record): void {
        $record->update(['status' => 'published']);

        Notification::make()
            ->title('Post published')
            ->success()
            ->send();
    })
Or use the built-in methods:
Action::make('publish')
    ->successNotificationTitle('Post published')
    ->action(fn (Post $record) => $record->update(['status' => 'published']))

Redirects

Redirect after action completion:
Action::make('create')
    ->form([...])
    ->action(function (array $data) {
        $post = Post::create($data);
        
        return redirect()->route('posts.edit', $post);
    })
Or use the redirect method:
Action::make('publish')
    ->action(fn (Post $record) => $record->update(['status' => 'published']))
    ->successRedirectUrl(fn (Post $record): string => route('posts.show', $record))

Build docs developers (and LLMs) love