Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Unsaved Changes Modal Laravel Package

azgasim/filament-unsaved-changes-modal

Replaces Filament's browser confirmation dialog with a native Filament modal when leaving a dirty form. Works with SPA and full-page navigation, using the same dirty-state rules as unsavedChangesAlerts(). Requires Filament 5.3.5+ and PHP 8.2+.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. The package is an extremely thin, surgical extension of an existing Filament capability rather than a new subsystem. It does not introduce middleware, a service provider contract, a facade, a database table, a queue job, or a config file. Its entire surface is one final plugin class (FilamentUnsavedChangesModalPlugin) with five fluent setters, plus a Blade hook that injects client-side interception JavaScript and a modal view keyed by a fixed DOM id (MODAL_DOM_ID). That means it composes with the panel provider the same way a theme or a color palette does — declarative, one wiring point, uniformly applied to every resource, custom page, and relation-manager form in that panel. For a TPM, the important architectural property is negative footprint: adopting it cannot perturb routing, auth, persistence, or the Livewire component tree. It only replaces the presentation of a confirmation that Filament already produces.

The dependency it leans on is the real architectural fact: the plugin is a UI swap over Filament's unsavedChangesAlerts(). Filament owns dirty-state detection; this package owns the dialog. That is a clean separation of concerns and a good sign for maintainability, but it also means the value proposition is bounded — you are buying styling and UX consistency, not capability. The underlying capability (confirm() on dirty navigation, browser prompt on reload/tab-close) exists for free in Filament 5.

Integration feasibility. High. Two commands and two chained lines in the panel provider. No migration path, no data model, no backfill, no feature flag machinery required. Both an existing panel and a greenfield panel adopt it identically. The data-skip-unsaved-changes-modal attribute (resolved via closest(...)) gives per-link and per-cluster opt-out without touching the plugin API, which covers the realistic edge cases: explicit "cancel and return to list" links, logout, help-desk links, and ephemeral preview toolbars. Localization is standard Laravel vendor-publish; en, de, ckb, and fr ship in the box.

The hard constraint is version: filament/filament ^5.3.5 and php ^8.2. The README is explicit that the floor exists because of a known XSS advisory (CVE-2026-33080) in earlier Filament 5.x, and the release notes mention Composer security-audit blocks under prefer-lowest. Practically, this package forces your panel to Filament 5.3.5+, which is either a non-issue (you are current) or a scoping decision (you are not). Any team still on Filament 3.x or 4.x is out of scope entirely, and the plugin offers no backport.

Technical risk. Moderate-low in blast radius, moderate in dependency risk. The specific risks a TPM should log:

Risk Assessment
Single maintainer, 12 stars, 0 dependents The dominant risk. Bus factor of one. The mitigating factor is that the package is small, MIT-licensed, and the critical logic is a single Blade/JS hook you could fork into your own repo in an afternoon. Treat it as vendorable source, not as critical infrastructure.
Silent misconfiguration Omitting ->unsavedChangesAlerts() produces no error, no log line, and no modal — the feature just quietly does nothing. This is the most likely production surprise and needs an explicit smoke test in CI, not a code review.
DOM id coupling The script opens and closes the modal by MODAL_DOM_ID. If you publish views and change the id, or if a future Filament theme collides, the modal silently fails to open while navigation is already suppressed — a potential "user is stuck on the form" failure.
JS interception correctness The listener must distinguish same-origin http(s) links from external, mailto:, tel:, download, and programmatic navigations. Release notes show hardening here (non-http(s) hrefs, same-origin checks, defensive try/catch), which is reassuring but also indicates this is the historically fragile surface.
Incomplete coverage by design Reload and tab-close still fall back to the browser's native prompt. That is a browser platform limit, not a package defect — but it means the UX is inconsistent, and stakeholders who see the styled modal will ask why the reload prompt looks different. Set that expectation up front.
Filament major-version treadmill Interception depends on Filament's livewire:navigate behavior and its Blade hook points. A Filament 6 upgrade is a likely breaking event with no guaranteed maintainer response.

Key questions to resolve before committing.

  1. Build vs. buy. This is roughly a modal view, a script hook, a plugin class, and two lang files. Is the ~100–200 lines of first-party code cheaper over 24 months than an external dependency with a bus factor of one? For most teams the honest answer is "buy, but vendor the source."
  2. Does it intercept the navigations you actually have? SPA links, same-origin full-page links, custom header actions, wizard-step buttons, and — critically — programmatic $this->redirect() / redirect() calls from Livewire actions. The latter is very likely not intercepted, and that is often the dominant navigation path on an edit page after save.
  3. Is the inline script CSP-compatible? A Blade-injected script hook may require a nonce or hash under a strict Content-Security-Policy. Verify against your actual headers, not a local dev environment.
  4. Accessibility. Does the modal trap focus, restore focus on close, and respond to Escape per Filament's own modal conventions? Since it reuses Filament modal primitives it probably inherits this — confirm rather than assume.
  5. Maintainer intent and responsiveness. Last release 2026-10-15 with steady small increments (1.0.0 → 1.0.1 → 1.0.2, community translation PRs merged). Is there a stated support window for Filament 5.x minors? Is there a security contact beyond SECURITY.md?
  6. What is the exit cost? If the maintainer disappears, can you pin the current version indefinitely on your Filament minor, or does the ^5.3.5 constraint drag you forward into an incompatible minor?

Integration Approach

Stack fit. The package targets exactly one stack shape: a Filament 5 panel (5.3.5+), PHP 8.2+, Composer-managed, using unsavedChangesAlerts() on the panel. If your application is a standard Laravel + Filament admin, fit is essentially perfect — the plugin registers on the panel object alongside your existing ->plugins([...]) array and inherits panel-wide scope. If you run multiple panels (admin, customer portal, partner console), each panel opts in independently, which is the right granularity: you can ship it on the internal admin panel first and leave customer-facing surfaces untouched.

Migration path. Not a migration in the data sense — a configuration change with a one-line removal as rollback. Concretely:

  1. composer require azgasim/filament-unsaved-changes-modal on a short-lived branch.
  2. Add ->unsavedChangesAlerts() (if absent) and ->plugin(FilamentUnsavedChangesModalPlugin::make()) to the target panel provider, colocated so the visual contract is one readable chain.
  3. Walk the highest-traffic dirty-form flows: edit user, edit order, a settings page, and any wizard-like custom page.
  4. Remove any temporary guard. Do not build a feature flag for this — a runtime-toggled config key is more maintenance than the feature is worth.

Rollback is deleting two chained lines and running composer remove. That reversibility is the strongest argument for trying it: the cost of being wrong is an afternoon, not a sprint.

Compatibility. The binding constraint is Filament's minor floor. Verify your composer.lock resolves filament/filament to >=5.3.5 before adding the package, because the plugin's constraint will otherwise force an upgrade of Filament itself as a side effect of a UI change — a scoping surprise in a release window. The PHP 8.2 floor is unremarkable for 2026. Note the 1.0.0 breaking change (published config file removed in favor of plugin-only configuration): if anyone prototypes from a pre-1.0 blog post or example, they will write a config file that is silently ignored.

Sequencing. Adopt in this order, because each step de-risks the next:

  • Phase 1 — Verify. Confirm Filament version floor, confirm unsavedChangesAlerts() state per panel, confirm CSP headers permit the injected script. All three are cheap checks that can kill the initiative early.
  • Phase 2 — Pilot one flow. One panel, one Edit page, one dirty-field, one sidebar click. Then a table row link (SPA path) into an edit page. Both green means interception is wired for both navigation styles.
  • Phase 3 — Theme and localize. Add the fluent chain; publish translations only if shipped locales are insufficient. If you have a non-en user base, audit the four shipped locales for tone and completeness before publishing overrides — the copy is user-facing and legally mundane but visible.
  • Phase 4 — Instrument the escape hatch. Add data-skip-unsaved-changes-modal to the short list of links where blocking is hostile (cancel/discard, logout, support). Keep it short; the feature's value erodes proportionally to bypass coverage.
  • Phase 5 — Lock in regression coverage. One browser/Dusk test per critical flow: dirty a form, click a nav link, assert the modal DOM id is present, assert Leave lands on the target URL, assert Stay leaves you on the form with data intact. That single test is your insurance across Filament minor upgrades and the primary defense against the silent-misconfiguration failure mode.
  • Phase 6 — Vendor contingency. Document, in the ADR, the fork-and-own procedure: which two files carry the logic, what MODAL_DOM_ID must stay in sync with, and who owns it if upstream goes quiet. Cheap to write, expensive to reconstruct during an incident.

Operational Impact

Maintenance. Near-zero steady state, with two named exceptions. First, translation drift: every new locale or copy change is a publish-and-edit cycle, and user-facing text in four languages with no owner becomes stale. Assign the published lang files to whoever owns panel copy. Second, Filament upgrade review: any Filament minor that touches modal internals, Blade hook ordering, or livewire:navigate semantics requires re-walking the pilot flows. Budget this as a line item in your Filament upgrade checklist rather than as a surprise. There is no config file to drift, no migrations to run, no scheduled task, no cache to clear — the maintenance surface is genuinely small.

Support. Low tier-1 volume, but with a specific and predictable shape: users who reload or close a tab will see the old browser prompt while everything else is styled. That is a browser limitation, not a bug, and it will generate tickets phrased as "the modal doesn't work sometimes." Pre-empt it with a short note in internal release communications. A second support pattern is the "stuck on form" report caused by a published-view id mismatch or a JS interception edge case; the triage path should be documented as "check MODAL_DOM_ID sync, then check the script hook view" — which is exactly the debugging order the implementation guidance recommends.

Scaling. Operationally flat. The cost is client-side only: one small script evaluated per panel page load, one modal rendered on demand. No server-side per-request overhead, no query cost, no memory footprint, no concurrency consideration. It scales to any number of resources and forms trivially because it applies at the panel level. The only "scaling" dimension that matters is panel count and team count — more panels means more registration sites to keep in sync, and more teams means more people who need to know the data-skip-unsaved-changes-modal convention exists. Address the first with a small private factory method or palette helper; address the second with a line in the frontend conventions doc.

Failure modes. Enumerated for the runbook, in descending likelihood:

Failure Symptom Detection Mitigation
unsavedChangesAlerts() missing Feature silently absent, no logs Smoke test in CI Explicit test; README's most-flagged gotcha
Published view id drift Modal never opens; navigation suppressed → user feels stuck Manual QA of dirty-form exit Keep id in sync with MODAL_DOM_ID; avoid publishing views unless necessary
JS interception too broad External links or downloads blocked or misrouted Link-audit test Rely on the same-origin / non-http(s) hardening in 1.0.0+; keep current
Programmatic redirect not intercepted Save-then-redirect bypasses the modal entirely Flow walkthrough Expected behavior for post-save; confirm it is acceptable UX
Filament minor upgrade breaks hook Modal stops appearing after upgrade Dusk regression test Pin Filament minor; re-walk flows on upgrade
Upstream abandonment No fixes for future Filament majors Quarterly dependency review Fork-and-own contingency documented; MIT license permits it
CSP violation on inline script Script blocked; feature absent; console errors CSP report-only rollout Nonce/hash the hook or verify compatibility in Phase 1

The compounding observation across all of these: every failure mode is a silent UI degradation, never a data-integrity or availability event. Worst case, users see the browser's confirm() again — which is the pre-adoption baseline. That asymmetry is what makes this a low-risk adoption for a TPM to approve.

Ramp-up. Effectively immediate for engineers — the README plus the single plugin class is a complete mental model, and the "first use case to validate" is spelled out (dirty an Edit page, click a sidebar link). The real ramp-up cost is organizational, not technical: (1) teaching the team that data-skip-unsaved-changes-modal exists and when not to use it, (2) teaching the two-or-three-person group who owns panel frontend that a Filament upgrade now includes a modal regression walk, and (3) writing the one Dusk test that converts "I think it works" into "CI knows it works." Total onboarding is measured in hours.

Recommendation. Adopt, with conditions: pin the Filament minor, vendor the source or document the fork procedure, add one browser-level regression test per critical dirty-form flow, and treat reload/tab-close inconsistency as an accepted, communicated limitation. The package buys genuine UX polish — a native Filament modal instead of a jarring browser confirm() — at a dependency cost that is small, reversible, and fully contained within the presentation layer. Given a score profile of high opportunity against a thin maturity signal (12 stars, 0 dependents, single maintainer), the correct posture is low-commitment adoption with a documented exit, not a strategic bet on the package's longevity.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package