wezlo/filament-modal-notifications
Turn any Filament notification into a blocking modal by chaining ->asModal(). Modal notifications queue (no stacking), auto-add a Close button when no actions are set, respect your custom actions, support conditional opt-in, and offer panel-level defaults.
Render any Filament notification as a blocking modal by chaining one method: ->asModal(). Multiple modal notifications fired in the same request are queued one at a time — the user dismisses one and the next slides in, no stacking.
Everything else you already know about Filament notifications (title, body, icon, color, actions, danger()/success()/warning()/info()) carries over unchanged.
->asModal() onto any Notification::make() call. No new classes to learn.->actions([...]), those render in the footer instead.->asModal($isCritical) is a no-op when the condition is false, so the notification sends as a regular toast.composer require wezlo/filament-modal-notifications
Register the plugin on your panel so the Livewire component mounts at the page footer:
use Wezlo\FilamentModalNotifications\FilamentModalNotificationsPlugin;
public function panel(Panel $panel): Panel
{
return $panel
->plugins([
FilamentModalNotificationsPlugin::make(),
]);
}
Optionally publish the config:
php artisan vendor:publish --tag=filament-modal-notifications-config
use Filament\Notifications\Notification;
Notification::make()
->title('Changes not saved')
->body('Your unsaved edits will be lost if you leave this page.')
->danger()
->asModal()
->send();
That's it. On the next Livewire cycle, a modal opens with the title, body, and a default Close button. Regular toasts still work exactly as before — only notifications with ->asModal() go through the modal pipeline.
The notification's actions render in the modal footer:
use Filament\Actions\Action;
use Filament\Notifications\Notification;
Notification::make()
->title('Delete this order?')
->body('This cannot be undone.')
->warning()
->actions([
Action::make('cancel')->label('Cancel')->color('gray')->close(),
Action::make('delete')->label('Delete')->color('danger')
->action(fn () => $this->record->delete())
->close(),
])
->asModal()
->send();
When any action's ->close() is invoked (or the user clicks X / Esc / clicks outside if closable), the queue advances to the next pending modal notification.
->asModal(false) leaves the notification as a normal toast:
Notification::make()
->title($wasCritical ? 'Error' : 'Saved')
->asModal($wasCritical)
->send();
Notification::make()->title('Step 1 complete')->asModal()->send();
Notification::make()->title('Step 2 complete')->asModal()->send();
Notification::make()->title('Step 3 complete')->asModal()->send();
The user sees one modal at a time in FIFO order. Each dismissal advances to the next. When the queue is empty the modal closes.
use Filament\Support\Enums\Width;
use Wezlo\FilamentModalNotifications\FilamentModalNotificationsPlugin;
->plugins([
FilamentModalNotificationsPlugin::make()
->defaultWidth(Width::Large)
->defaultClosable(false) // block Esc / click-away / X — require an explicit action
->defaultCloseButtonLabel('Dismiss'),
])
| Method | Type | Default | Description |
|---|---|---|---|
defaultWidth(Width|string) |
enum/string | md |
Modal width (sm, md, lg, xl, 2xl, …, screen) |
defaultClosable(bool) |
bool | true |
Whether the modal can be dismissed via X, Esc, or click-away |
defaultCloseButtonLabel(string) |
string | Close |
Label for the auto-added Close action |
// config/filament-modal-notifications.php
return [
'default_width' => 'md',
'default_closable' => true,
'default_close_label' => 'Close',
];
->asModal() is a macro registered on Filament\Notifications\Notification in the service provider. It returns a Wezlo\FilamentModalNotifications\ModalNotification — a subclass that overrides send() to push into a dedicated session key (filament.modal-notifications) instead of the default filament.notifications.modalNotificationsSent Livewire event when the modal session key has pending notifications (same pattern Filament uses for its toast tray).Wezlo\FilamentModalNotifications\Livewire\ModalNotifications) mounts at PanelsRenderHook::BODY_END. It listens for modalNotificationsSent, drains the session key into an in-component queue, and renders the first pending notification through <x-filament::modal>. When the user dismisses the modal (X, Esc, click-away, or any action chained with ->close()), the component advances to the next queued notification.MIT
How can I help you explore Laravel packages today?