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
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,
]);
}
Update Edit page
use Filament\Actions;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
Actions\ForceDeleteAction::make(),
Actions\RestoreAction::make(),
];
}
Soft Delete Actions
Delete
Force Delete
Restore
Soft deletes the record:Actions\DeleteAction::make()
The record can still be restored. Permanently removes the record:Actions\ForceDeleteAction::make()
This cannot be undone. Restores a soft-deleted record:Actions\RestoreAction::make()
Only visible for trashed records.
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());
})
By default, bulk delete loads all records into memory to:
- Authorize each record individually (when using
authorizeIndividualRecords())
- 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);
}