Filament v3 Bulk Actions: Jobs, Progress &amp; Confirmation | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. Filament v3 Table Bulk Actions: Custom Confirmation, Progress Feedback, and Job Dispatch

 Filament v3 Table Bulk Actions: Custom Confirmation, Progress Feedback, and Job Dispatch
=========================================================================================

 Go beyond the default delete bulk action. Learn how to build Filament v3 bulk actions with custom confirmation modals, real-time progress via job batching, and safe queue dispatch patterns that survive large datasets.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 11 Jul 2026 · Updated 11 Jul 2026 · 3 min read

ShareCopy linkCopied

 ![Filament v3 Table Bulk Actions: Custom Confirmation, Progress Feedback, and Job Dispatch](https://cdn.msaied.com/412/68d290c667a619900211863678fa0b1f.png) 

  On this page +1. [The Problem With Default Bulk Actions](#the-problem-with-default-bulk-actions)
2. [1. Custom Confirmation Modal With Extra Fields](#1-custom-confirmation-modal-with-extra-fields)
3. [2. Dispatching a Laravel Job Batch](#2-dispatching-a-laravel-job-batch)
4. [3. Real-Time Progress Feedback](#3-real-time-progress-feedback)
5. [4. Safety Details Worth Getting Right](#4-safety-details-worth-getting-right)
6. [Takeaways](#takeaways)

 The Problem With Default Bulk Actions
-------------------------------------

Filament ships with `DeleteBulkAction` and a handful of helpers, but real applications need bulk actions that:

- Ask for extra input before running (e.g. a reason, a target status)
- Dispatch work to a queue instead of blocking the request
- Show the user meaningful feedback when the batch finishes

This article walks through all three concerns with production-ready code.

---

1. Custom Confirmation Modal With Extra Fields
----------------------------------------------

The `requiresConfirmation()` helper gives you a yes/no dialog. For richer input, swap to `form()` on the action:

```php
BulkAction::make('archive')
    ->label('Archive Selected')
    ->icon('heroicon-o-archive-box')
    ->form([
        Textarea::make('reason')
            ->label('Archive reason')
            ->required()
            ->maxLength(500),
    ])
    ->action(function (Collection $records, array $data): void {
        ArchiveRecordsJob::dispatch(
            $records->modelKeys(),
            $data['reason'],
            auth()->id(),
        );
    })
    ->deselectRecordsAfterCompletion()
    ->successNotificationTitle('Archive queued')
    ->color('warning');

```

The `form()` closure receives validated `$data` alongside `$records`. Filament renders the fields inside the confirmation modal automatically — no custom Livewire component needed.

---

2. Dispatching a Laravel Job Batch
----------------------------------

Passing raw model keys (not Eloquent models) to the job keeps the serialized payload small and avoids stale model state:

```php
// app/Jobs/ArchiveRecordsJob.php
final class ArchiveRecordsJob implements ShouldQueue
{
    use Queueable, Dispatchable, InteractsWithQueue, SerializesModels;

    public function __construct(
        private readonly array $ids,
        private readonly string $reason,
        private readonly int $actorId,
    ) {}

    public function handle(): void
    {
        Post::whereIn('id', $this->ids)
            ->lazyById(200)
            ->each(function (Post $post): void {
                $post->archive($this->reason, $this->actorId);
            });
    }
}

```

For very large selections, split into a **job batch** so each chunk runs independently and failures are isolated:

```php
->action(function (Collection $records, array $data): void {
    $chunks = array_chunk($records->modelKeys(), 100);

    $batch = Bus::batch(
        collect($chunks)->map(
            fn (array $ids) => new ArchiveRecordsJob($ids, $data['reason'], auth()->id())
        )->all()
    )
    ->name('archive-posts-' . now()->timestamp)
    ->allowFailures()
    ->dispatch();

    // Persist batch ID so the UI can poll it
    session()->put('archive_batch_id', $batch->id);
})

```

---

3. Real-Time Progress Feedback
------------------------------

Store the batch ID in the session (or a DB record keyed to the user) and expose a Livewire polling component in your Filament page footer via a render hook:

```php
// AppServiceProvider::boot()
Filament::registerRenderHook(
    PanelsRenderHook::BODY_END,
    fn (): View => view('filament.batch-progress'),
);

```

```xml
{{-- resources/views/filament/batch-progress.blade.php --}}
@if(session('archive_batch_id'))

    @livewire('batch-progress-indicator', [
        'batchId' => session('archive_batch_id')
    ])

@endif

```

The Livewire component calls `Bus::findBatch($this->batchId)` and exposes `$batch->progress()` (0–100) and `$batch->finished()`. When finished, dispatch a browser event to trigger a Filament notification and clear the session key.

---

4. Safety Details Worth Getting Right
-------------------------------------

**Authorization** — always gate the action:

```php
->authorize(fn (): bool => auth()->user()->can('archive', Post::class))

```

**Chunk size** — `lazyById` inside the job prevents loading thousands of models into memory at once. Tune the chunk size to your row width.

**`allowFailures()`** — without this, a single failing job cancels the entire batch. For archiving, partial success is usually acceptable; log failures via `->catch()` on the batch.

**Idempotency** — if a job retries, re-archiving an already-archived post should be a no-op. Guard with a status check at the top of `handle()`.

---

Takeaways
---------

- Use `form()` on `BulkAction` for confirmation modals that collect extra input before dispatch.
- Pass model keys (not models) to queued jobs to keep payloads lean.
- Split large selections into a `Bus::batch()` with `allowFailures()` for resilient processing.
- Persist the batch ID and poll `Bus::findBatch()` in a Livewire component for live progress.
- Always authorize bulk actions explicitly; Filament does not infer policy checks automatically.

- [filament](https://www.msaied.com/public/articles?search=filament)
- [laravel](https://www.msaied.com/public/articles?search=laravel)
- [queues](https://www.msaied.com/public/articles?search=queues)
- [livewire](https://www.msaied.com/public/articles?search=livewire)

 Frequently asked questions 
---------------------------

  Can I access the form data inside the bulk action's `action()` closure?Yes. When you define a `form()` on a BulkAction, Filament injects a validated `array $data` parameter alongside `Collection $records` in the `action()` closure. The keys match the field names you declared in the form.

   How do I prevent the bulk action from timing out on very large selections?Dispatch a queued job (or a Bus batch of jobs) from the action closure instead of processing records inline. The HTTP request returns immediately after dispatch, and the heavy work runs on your queue workers. Use `lazyById()` inside the job to avoid loading all records into memory at once.

   Does `deselectRecordsAfterCompletion()` work when the action dispatches a job?Yes. It deselects the checkboxes on the client side as soon as the action closure returns, regardless of whether the actual work is synchronous or queued. It is purely a UI concern.

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

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

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articlePractical RAG in Laravel: pgvector, Embeddings, and Retrieval Pipelines](https://www.msaied.com/public/articles/practical-rag-in-laravel-pgvector-embeddings-and-retrieval-pipelines-1) [Next articleLaravel Observers vs. Model Events: Choosing the Right Hook for Side Effects](https://www.msaied.com/public/articles/laravel-observers-vs-model-events-choosing-the-right-hook-for-side-effects-1)  

   On this page
-------------

1. [The Problem With Default Bulk Actions](#the-problem-with-default-bulk-actions)
2. [1. Custom Confirmation Modal With Extra Fields](#1-custom-confirmation-modal-with-extra-fields)
3. [2. Dispatching a Laravel Job Batch](#2-dispatching-a-laravel-job-batch)
4. [3. Real-Time Progress Feedback](#3-real-time-progress-feedback)
5. [4. Safety Details Worth Getting Right](#4-safety-details-worth-getting-right)
6. [Takeaways](#takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
