Skip to main content

Delete Actions

Filament provides multiple ways to delete records:
  • Single delete - Delete button on Edit/View pages
  • Bulk delete - Select multiple records and delete them at once
  • Table row delete - Delete action in table rows

Deleting from Edit/View Pages

Add a delete action to the page header:
use Filament\Actions;

protected function getHeaderActions(): array
{
    return [
        Actions\DeleteAction::make(),
    ];
}

Deleting from the List Page

Delete individual records from the table:
use Filament\Actions\DeleteAction;
use Filament\Tables\Table;

public static function table(Table $table): Table
{
    return $table
        ->columns([
            // ...
        ])
        ->recordActions([
            DeleteAction::make(),
        ]);
}

Bulk Delete

Bulk delete is available by default through toolbar actions:
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Tables\Table;

public static function table(Table $table): Table
{
    return $table
        ->columns([
            // ...
        ])
        ->toolbarActions([
            BulkActionGroup::make([
                DeleteBulkAction::make(),
            ]),
        ]);
}

Soft Deletes

Laravel’s soft delete functionality allows “deleting” records without actually removing them from the database.

Creating a Resource with Soft Deletes

php artisan make:filament-resource Customer --soft-deletes
This generates a resource with:
  • Force delete actions
  • Restore actions
  • Trashed filter

Adding Soft Deletes to Existing Resources

1

Update the table

use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteAction;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\ForceDeleteAction;
use Filament\Actions\ForceDeleteBulkAction;
use Filament\Actions\RestoreAction;
use Filament\Actions\RestoreBulkAction;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;

public static function table(Table $table): Table
{
    return $table
        ->columns([
            // ...
        ])
        ->filters([
            TrashedFilter::make(),
        ])
        ->recordActions([
            DeleteAction::make(),
            ForceDeleteAction::make(),
            RestoreAction::make(),
        ])
        ->toolbarActions([
            BulkActionGroup::make([
                DeleteBulkAction::make(),
                ForceDeleteBulkAction::make(),
                RestoreBulkAction::make(),
            ]),
        ]);
}

public static function getRecordRouteBindingEloquentQuery(): Builder
{
    return parent::getRecordRouteBindingEloquentQuery()
        ->withoutGlobalScopes([
            SoftDeletingScope::class,
        ]);
}
2

Update Edit page

use Filament\Actions;

protected function getHeaderActions(): array
{
    return [
        Actions\DeleteAction::make(),
        Actions\ForceDeleteAction::make(),
        Actions\RestoreAction::make(),
    ];
}

Soft Delete Actions

Soft deletes the record:
Actions\DeleteAction::make()
The record can still be restored.

Trashed Filter

The trashed filter allows users to view:
  • Active records only (default)
  • Trashed records only
  • All records (active and trashed)
use Filament\Tables\Filters\TrashedFilter;

$table->filters([
    TrashedFilter::make(),
])

Authorization

Filament observes model policy methods for delete operations:

Single Delete

public function delete(User $user, Customer $customer): bool
{
    return $user->can('delete customers');
}

Bulk Delete

public function deleteAny(User $user): bool
{
    return $user->can('delete customers');
}
Filament uses deleteAny() for bulk operations because checking delete() for each record is not performant.

Check Individual Records in Bulk Delete

To check the delete() policy for each record:
DeleteBulkAction::make()
    ->authorizeIndividualRecords()
This may cause performance issues with large selections.

Force Delete Authorization

public function forceDelete(User $user, Customer $customer): bool
{
    return $user->can('force delete customers');
}

public function forceDeleteAny(User $user): bool
{
    return $user->can('force delete customers');
}

Restore Authorization

public function restore(User $user, Customer $customer): bool
{
    return $user->can('restore customers');
}

public function restoreAny(User $user): bool
{
    return $user->can('restore customers');
}

Customizing Delete Actions

Confirmation Modal

Customize the delete confirmation:
Actions\DeleteAction::make()
    ->modalHeading('Delete customer')
    ->modalDescription('Are you sure you want to delete this customer? This action cannot be undone.')
    ->modalSubmitActionLabel('Yes, delete it')

Requiring Confirmation

Require the user to type a word:
Actions\DeleteAction::make()
    ->requiresConfirmation()

Success Notification

use Filament\Notifications\Notification;

Actions\DeleteAction::make()
    ->successNotification(
        Notification::make()
            ->success()
            ->title('Customer deleted')
            ->body('The customer has been deleted successfully.')
    )

Before/After Hooks

Actions\DeleteAction::make()
    ->before(function (Customer $record) {
        // Log the deletion
        activity()
            ->performedOn($record)
            ->log('Customer deleted');
    })
    ->after(function () {
        // Send notification
        Mail::to(auth()->user())
            ->send(new CustomerDeletedEmail());
    })

Bulk Delete Performance

By default, bulk delete loads all records into memory to:
  1. Authorize each record individually (when using authorizeIndividualRecords())
  2. Fire model events (deleting, deleted)

Chunking Records

Process records in smaller batches:
DeleteBulkAction::make()
    ->chunkSelectedRecords(250)

Skip Fetching Records

For maximum performance, skip loading records entirely:
DeleteBulkAction::make()
    ->fetchSelectedRecords(false)
This bypasses individual authorization checks and model events.

Preventing Accidental Deletion

Add extra safeguards for important data:
use Filament\Actions\DeleteAction;

DeleteAction::make()
    ->requiresConfirmation()
    ->modalIcon('heroicon-o-exclamation-triangle')
    ->modalIconColor('danger')
    ->color('danger')

Lifecycle Events

Listen to model events in observers:
class CustomerObserver
{
    public function deleting(Customer $customer): void
    {
        // Before soft delete
        activity()
            ->performedOn($customer)
            ->log('Customer is being deleted');
    }
    
    public function deleted(Customer $customer): void
    {
        // After soft delete
        Cache::forget("customer.{$customer->id}");
    }
    
    public function forceDeleting(Customer $customer): void
    {
        // Before permanent deletion
        $customer->orders()->delete();
    }
    
    public function restored(Customer $customer): void
    {
        // After restoration
        Notification::make()
            ->success()
            ->title('Customer restored')
            ->send();
    }
}
Register the observer:
use App\Models\Customer;
use App\Observers\CustomerObserver;

public function boot(): void
{
    Customer::observe(CustomerObserver::class);
}

Build docs developers (and LLMs) love