Filament v4 Bulk Actions: Modals and Authorization | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com) [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles  ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

  [ home ](https://www.msaied.com)    [ articles ](https://www.msaied.com/articles)    Filament v4 Table Bulk Actions: Custom Confirmation Modals and Scoped Authorization        On this page       1. [  Why Bulk Actions Deserve More Attention ](#why-bulk-actions-deserve-more-attention)
2. [  Defining a Custom Bulk Action ](#defining-a-custom-bulk-action)
3. [  Per-Record Authorization Inside the Action ](#per-record-authorization-inside-the-action)
4. [  Adding a Custom Confirmation Form ](#adding-a-custom-confirmation-form)
5. [  Chunking Large Selections Safely ](#chunking-large-selections-safely)
6. [  Key Takeaways ](#key-takeaways)

  ![Filament v4 Table Bulk Actions: Custom Confirmation Modals and Scoped Authorization](https://cdn.msaied.com/661/5f319b485f1bc0c76e2c82746f730c8c.png)

  #filament   #laravel   #authorization   #queues  

 Filament v4 Table Bulk Actions: Custom Confirmation Modals and Scoped Authorization 
=====================================================================================

     12 Sep 2026      4 min read    ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said  

       Table of contents

1. [  01   Why Bulk Actions Deserve More Attention  ](#why-bulk-actions-deserve-more-attention)
2. [  02   Defining a Custom Bulk Action  ](#defining-a-custom-bulk-action)
3. [  03   Per-Record Authorization Inside the Action  ](#per-record-authorization-inside-the-action)
4. [  04   Adding a Custom Confirmation Form  ](#adding-a-custom-confirmation-form)
5. [  05   Chunking Large Selections Safely  ](#chunking-large-selections-safely)
6. [  06   Key Takeaways  ](#key-takeaways)

 Why Bulk Actions Deserve More Attention
---------------------------------------

Most Filament tutorials show `DeleteBulkAction` and move on. In real applications, bulk actions are where business logic concentrates — mass approvals, batch exports, multi-record state transitions — and they need proper authorization, user feedback, and reliable async dispatch.

This article focuses on Filament v4's bulk action API, which ships with the unified Schema system and a cleaner modal builder.

---

Defining a Custom Bulk Action
-----------------------------

Register bulk actions inside `table()` on your resource's `ListRecords` page or directly in the `Table` definition:

```php
use Filament\Tables\Actions\BulkAction;
use Illuminate\Database\Eloquent\Collection;

BulkAction::make('approve')
    ->label('Approve Selected')
    ->icon('heroicon-o-check-circle')
    ->color('success')
    ->requiresConfirmation()
    ->modalHeading('Approve Applications')
    ->modalDescription('Only applications you are authorised to approve will be processed.')
    ->modalSubmitActionLabel('Approve Now')
    ->action(function (Collection $records): void {
        $records
            ->filter(fn ($r) => auth()->user()->can('approve', $r))
            ->each(fn ($r) => ApproveApplicationJob::dispatch($r));
    })
    ->deselectRecordsAfterCompletion()

```

The `requiresConfirmation()` call renders a modal automatically. The `modalDescription` string is the right place to set user expectations about partial authorization.

---

Per-Record Authorization Inside the Action
------------------------------------------

Filament's `authorize()` callback controls whether the bulk action *button* is visible, but it does not filter individual records. Always filter inside `action()` itself:

```php
->authorize(fn (): bool => auth()->user()->can('approve-applications'))
->action(function (Collection $records): void {
    $authorized = $records->filter(
        fn ($record) => auth()->user()->can('approve', $record)
    );

    if ($authorized->isEmpty()) {
        Notification::make()
            ->title('Nothing to approve')
            ->warning()
            ->send();
        return;
    }

    $authorized->each(fn ($r) => ApproveApplicationJob::dispatch($r));

    Notification::make()
        ->title("{$authorized->count()} application(s) queued for approval")
        ->success()
        ->send();
})

```

This pattern prevents privilege escalation when a user selects records they own alongside records they do not.

---

Adding a Custom Confirmation Form
---------------------------------

Sometimes you need extra input before the action runs — a reason field, a scheduled date, a confirmation string. Filament v4 lets you attach a Schema-based form to any bulk action modal:

```php
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\DatePicker;

BulkAction::make('schedule_review')
    ->label('Schedule Review')
    ->form([
        DatePicker::make('review_date')
            ->label('Review Date')
            ->required()
            ->minDate(today()->addDay()),
        Textarea::make('notes')
            ->label('Internal Notes')
            ->maxLength(500),
    ])
    ->action(function (Collection $records, array $data): void {
        $records->each(
            fn ($r) => $r->update([
                'review_date' => $data['review_date'],
                'review_notes' => $data['notes'],
                'status' => 'pending_review',
            ])
        );
    })
    ->deselectRecordsAfterCompletion()

```

The `$data` array is automatically validated against the form rules before `action()` fires.

---

Chunking Large Selections Safely
--------------------------------

When a user selects thousands of records, loading them all into a `Collection` will exhaust memory. Dispatch a single job that re-queries by IDs instead:

```php
->action(function (Collection $records): void {
    $ids = $records->modelKeys();

    ProcessBulkApprovalJob::dispatch($ids, auth()->id());

    Notification::make()
        ->title('Bulk approval queued')
        ->body(count($ids) . ' records will be processed in the background.')
        ->success()
        ->send();
})

```

Inside `ProcessBulkApprovalJob`, chunk the IDs:

```php
public function handle(): void
{
    $approver = User::findOrFail($this->approverId);

    collect($this->ids)->chunk(100)->each(function ($chunk) use ($approver) {
        Application::whereIn('id', $chunk)
            ->get()
            ->filter(fn ($r) => $approver->can('approve', $r))
            ->each(fn ($r) => $r->markApproved($approver));
    });
}

```

---

Key Takeaways
-------------

- Always filter records by policy inside `action()` — the `authorize()` callback only gates visibility.
- Use `->form([...])` on a `BulkAction` to collect structured input before execution.
- For large selections, pass only IDs to a queued job and re-query with chunking.
- `deselectRecordsAfterCompletion()` prevents stale checkbox state after async operations.
- Send contextual `Notification` feedback so users know when partial authorization reduced the scope.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Ffilament-v4-table-bulk-actions-custom-confirmation-modals-and-scoped-authorization&text=Filament+v4+Table+Bulk+Actions%3A+Custom+Confirmation+Modals+and+Scoped+Authorization) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Ffilament-v4-table-bulk-actions-custom-confirmation-modals-and-scoped-authorization) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  Does Filament's authorize() callback on a BulkAction filter individual records?        No. The authorize() callback only controls whether the action button is rendered for the current user. You must filter individual records inside the action() closure using your policy or gate checks. 

      Q02  How do I pass extra user input to a Filament bulk action?        Attach a -&gt;form([...]) array of Filament form components to the BulkAction. The validated data is injected as a $data array into the action() closure automatically. 

      Q03  What is the safest way to handle bulk actions on thousands of records in Filament?        Extract only the record IDs from the Collection using modelKeys(), then dispatch a single queued job with those IDs. Inside the job, re-query and chunk the IDs to avoid memory exhaustion. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://www.msaied.com/articles) 

 [ ![Laravel Horizon: Queue Metrics, Supervisor Tuning, and Safe Deployments](https://cdn.msaied.com/660/4e7fb1097d66f5b5c6bb68e5aad9b211.png) laravel horizon queues 

### Laravel Horizon: Queue Metrics, Supervisor Tuning, and Safe Deployments

Beyond the dashboard: how to use Horizon's metrics API, tune supervisor processes for mixed workloads, and dep...

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 12 Sep 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-horizon-queue-metrics-supervisor-tuning-and-safe-deployments) [ ![Custom Eloquent Casts: Value Objects, Enums, and Encrypted Composites](https://cdn.msaied.com/659/44f701e1dc43e64d0b7ecc984d0b34bc.png) laravel eloquent ddd 

### Custom Eloquent Casts: Value Objects, Enums, and Encrypted Composites

Go beyond primitive storage with Eloquent's CastsAttributes contract. Build reusable value-object casts, handl...

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 12 Sep 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/custom-eloquent-casts-value-objects-enums-and-encrypted-composites) [ ![Laravel Macro-Free Extensibility: Custom Query Builder Classes and Fluent Scopes](https://cdn.msaied.com/658/b45c3cc06b92a332e526bed9bb1f826d.png) laravel eloquent architecture 

### Laravel Macro-Free Extensibility: Custom Query Builder Classes and Fluent Scopes

Skip global macros and reach for typed, testable custom query builder classes in Laravel. Learn how to bind a...

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 11 Sep 2026     1 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-macro-free-extensibility-custom-query-builder-classes-and-fluent-scopes) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://www.msaied.com)
- [Projects](https://www.msaied.com/projects)
- [Articles](https://www.msaied.com/articles)
- [Certificates](https://www.msaied.com/certificates)
- [Contact](https://www.msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
