wezlo / filament-modal-notifications
Render any Filament notification as a blocking modal by chaining ->asModal() onto the native Notification API.
Package info
github.com/mustafakhaleddev/filament-modal-notifications
pkg:composer/wezlo/filament-modal-notifications
Requires
- php: ^8.2
- filament/filament: ^4.0 || ^5.0
- spatie/laravel-package-tools: ^1.0
This package is auto-updated.
Last update: 2026-04-24 10:39:10 UTC
README
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.
Requirements
- PHP 8.2+
- Laravel 11+ / 13
- Filament 4 or 5
Features
- One-line opt-in — chain
->asModal()onto anyNotification::make()call. No new classes to learn. - Queued — multiple modal notifications show one at a time, dismissal advances to the next.
- Auto-added Close button — if the developer didn't declare actions, the modal gets a "Close" button in its footer so it's always dismissible.
- Respects your actions — if you declare
->actions([...]), those render in the footer instead. - Conditional —
->asModal($isCritical)is a no-op when the condition is false, so the notification sends as a regular toast. - Independent of the toast tray — modal notifications never briefly flash as toasts; they use a dedicated session key and Livewire event.
- Plugin-level defaults — set the default modal width, closable behavior, and close button label per panel.
Installation
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
Quick Start
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.
Usage
With custom actions
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.
Conditional modal
->asModal(false) leaves the notification as a normal toast:
Notification::make() ->title($wasCritical ? 'Error' : 'Saved') ->asModal($wasCritical) ->send();
Queued — multiple in one request
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.
Panel-level defaults
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 |
Default Config File
// config/filament-modal-notifications.php return [ 'default_width' => 'md', 'default_closable' => true, 'default_close_label' => 'Close', ];
How It Works
->asModal()is a macro registered onFilament\Notifications\Notificationin the service provider. It returns aWezlo\FilamentModalNotifications\ModalNotification— a subclass that overridessend()to push into a dedicated session key (filament.modal-notifications) instead of the defaultfilament.notifications.- A custom dehydrate hook dispatches the
modalNotificationsSentLivewire event when the modal session key has pending notifications (same pattern Filament uses for its toast tray). - A Livewire component (
Wezlo\FilamentModalNotifications\Livewire\ModalNotifications) mounts atPanelsRenderHook::BODY_END. It listens formodalNotificationsSent, 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. - No toast flash — because modal notifications use a separate session key, the default toast tray never sees them.
License
MIT