azgasim / filament-unsaved-changes-modal
Filament plugin: unsaved-changes prompts as Filament modals instead of browser confirm dialogs.
Package info
github.com/AzGasim/filament-unsaved-changes-modal
Language:Blade
pkg:composer/azgasim/filament-unsaved-changes-modal
Fund package maintenance!
Requires
- php: ^8.2
- filament/filament: ^5.3.5
- spatie/laravel-package-tools: ^1.15.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.0
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^3.7|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
README
Filament Unsaved Changes Modal
In the Filament panel, leaving a dirty form shows a Filament modal instead of the browser’s blocking confirmation dialog.
Reloading or closing the tab still uses the native browser prompt (which cannot be customized).
Works with Filament SPA (livewire:navigate) and normal full-page navigation (same-origin link clicks in the panel body). Uses the same dirty-state rules as Filament’s unsavedChangesAlerts(); this package only swaps the confirmation UI.
Compatibility
| Plugin | Filament | PHP |
|---|---|---|
| 1.x | ^5.3.5 | ^8.2 |
Requires Filament 5.3.5+ due to a known XSS vulnerability (CVE-2026-33080) in earlier versions.
Installation
composer require azgasim/filament-unsaved-changes-modal
Usage
You need both unsavedChangesAlerts() and this plugin on the same panel.
use AzGasim\FilamentUnsavedChangesModal\FilamentUnsavedChangesModalPlugin; return $panel // … your existing panel configuration … ->unsavedChangesAlerts() ->plugin(FilamentUnsavedChangesModalPlugin::make());
You can also register the plugin inside ->plugins([...]) with your other plugins.
Customization
Modal appearance
Defaults live on [FilamentUnsavedChangesModalPlugin](src/FilamentUnsavedChangesModalPlugin.php): DEFAULT_MODAL_WIDTH, DEFAULT_MODAL_ICON_COLOR, DEFAULT_STAY_BUTTON_COLOR, DEFAULT_LEAVE_BUTTON_COLOR, and an icon default of OutlinedExclamationTriangle when you do not call modalIcon().
FilamentUnsavedChangesModalPlugin::make() ->modalWidth('xl') ->modalIcon('OutlinedExclamationTriangle') ->modalIconColor('danger') ->stayButtonColor('gray') ->leaveButtonColor('warning')
| Method | Values |
|---|---|
modalWidth() |
xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl, 7xl, full, min, max, fit, prose, screen-sm, screen-md, screen-lg, screen-xl, screen-2xl, screen — same string values as Filament’s Width enum (Filament\Support\Enums\Width). |
modalIcon() |
Heroicon::OutlinedExclamationTriangle or 'OutlinedExclamationTriangle' (not o-exclamation-triangle) |
modalIconColor(), stayButtonColor(), leaveButtonColor() |
primary, success, danger, warning, info, gray, … (your panel color keys) |
There is no published config file; configure the plugin with the methods above.
Translations
Heading, description, and the Stay / Leave labels are only customizable via translations (no plugin methods for copy). Publish and edit unsaved-changes-modal.php under your locale — keys live in navigation.*; see English (package also ships de).
php artisan vendor:publish --tag="filament-unsaved-changes-modal-translations"
Views
Only if you want to change the Blade markup, copy the package views into your app:
php artisan vendor:publish --tag="filament-unsaved-changes-modal-views"
If you change the modal’s DOM id in a published view, keep it in sync with [MODAL_DOM_ID](src/FilamentUnsavedChangesModalPlugin.php) and the script hook view (the script opens and closes the modal by that id).
Skipping the prompt for a link
Add data-skip-unsaved-changes-modal on the <a> or on any ancestor element (the listener uses closest(...)).
Testing
composer test
