defstudio/filament-dynamic-actions
Filament Dynamic Actions for Laravel lets you define configurable actions at runtime and attach them to tables, forms, or pages without hardcoding each one, keeping your Filament admin panel flexible, reusable, and easy to extend.
This is a Filament plugin (defstudio/filament-dynamic-actions) that lets you define actions in one place and dispatch them by name from anywhere — Livewire components, Blade, or JS — instead of wiring ->action(fn () => ...) closures at every call site.
Minimal setup:
composer require defstudio/filament-dynamic-actions:^4.0
php artisan vendor:publish --tag="filament-dynamic-actions-config"
Register your named actions in a service provider (or the package's config, depending on version), then attach them to a Filament component with ->dynamicAction('name') and trigger from the frontend.
Where to look first:
vendor/defstudio/filament-dynamic-actions/src — the DynamicAction class and the dispatch/validation handler are the two files you'll actually read.First use case: a table row action (e.g. delete) declared once with its confirmation modal and authorization, then re-used from a second place (a bulk action, a Blade button, a JS-triggered $dispatch) without duplicating the closure.
Version note for this release: v4.0.6 is a patch — it tightens the validation-check logic that runs on dynamic action dispatches. No API changes listed, so ^4.0 is a safe constraint; you get the fix on composer update with no code edits.
Register once, dispatch by name. The core win is a single registry of named actions:
// config/filament-dynamic-actions.php or a service provider
DynamicAction::make('delete-user')
->label('Delete')
->icon('heroicon-o-trash')
->requiresConfirmation()
->action(fn (User $user) => $user->delete());
Then reuse the name across a table action, a page header action, and a Blade-rendered button — all inherit the same label, icon, confirmation, and authorization in one edit.
Payload dispatch with validated params. The 4.0.6 fix affects dispatches that carry parameters: the package now checks the incoming payload against the action's expected signature/schema before invoking it. Prefer typed closures and explicit params over a single array $data bag — you get the validation for free and the failure mode is a clean rejection instead of a TypeError inside your handler.
DynamicAction::make('assign-role')
->action(fn (User $user, string $role) => $user->assignRole($role));
Wire it from Livewire with a dispatch carrying the name and params, and from Blade/JS with the package's directive when a full Livewire round-trip isn't needed. Keep the action name as a constant or enum on the PHP side so a rename is a single find-and-replace rather than a silent no-op at runtime.
Compose with authorization, not around it. Put ->visible() / policy checks inside the registered action so every dispatch surface is guarded identically. If you gate at the call site, you'll eventually miss one — and because the action is reachable by name from the frontend, an unguarded registration is a real exposure.
Test the dispatch, not just the closure. A feature test that fires the dynamic dispatch with a valid and an invalid payload covers the code path this release changed, and is the fastest way to catch a regression on future 4.x bumps.
Collection of Eloquent models with loaded relations. Pass IDs and re-resolve in the action; you get fresh models and avoid stale-state bugs.php artisan about/config cache show it? (2) Does the frontend dispatch carry the right name and params? (3) Does the action's own closure run when invoked directly in tinker? Each answer eliminates one of the three layers.php artisan config:cache in deploy is fine, but a stale cache across a release is a common "my new action doesn't exist" cause. Clear it in the deploy step.actions/checkout 7, esbuild 0.28.1, dependabot/fetch-metadata 3.1.0, ramsey/composer-install 4, git-auto-commit-action 7) — none of those touch your runtime. Run your dispatch tests after the bump; if you relied on loosely-typed payloads being passed through unchecked, tighten the action signature now.How can I help you explore Laravel packages today?