Skip to content

[6.x] Dirty state fixes#14592

Merged
jasonvarga merged 3 commits into6.xfrom
catch-browser-back-button
May 1, 2026
Merged

[6.x] Dirty state fixes#14592
jasonvarga merged 3 commits into6.xfrom
catch-browser-back-button

Conversation

@jackmcdade
Copy link
Copy Markdown
Member

@jackmcdade jackmcdade commented May 1, 2026

Closes #14055.

Three related fixes around dirty form state in the CP.

Browser back/forward now prompts on unsaved changes

Inertia's popstate handler swaps pages via page.setQuietly(..., { preserveState: false }) without firing its inertia:before event, so dirty-state.js never had a chance to warn. window.onbeforeunload doesn't fire for SPA history navigation either, which is why the trackpad swipe-back gesture (and the actual back/forward buttons) silently nuked unsaved form data.

dirty-state.js now registers a popstate listener at module load — before createInertiaApp() calls eventHandler.init() — so our handler runs first and stopImmediatePropagation() blocks Inertia from wiping the form. We capture the dirty page's URL/state on the 0→1 add() transition (so confirm() can keep the URL bar in place during the prompt and after a cancel). Confirm → history.back() into Inertia's normal flow; cancel → user stays put, form stays intact.

Container cleans up its dirty entry on unmount

Previously, the dirty ref was module-level state that outlived the form component. When Inertia silently swapped pages on popstate, the form was destroyed but its dirty entry remained — so the next Inertia Link click would prompt you about a form that didn't exist anymore. Container.vue now calls clearDirtyState() in onUnmounted, which is the right place to remove an entry the Container itself added.

disableWarning() actually allows re-enabling

disableWarning() called the unsubscribe but kept inertiaWarningListener set to the (already-called) function, so the ??= in enableWarning() would skip re-subscribing on the next dirty cycle. It now nulls the variable.

Closes  #14055

Intercept popstate in `dirty-state.js` before Inertia's listener so
back/forward navigation (including trackpad swipe gestures) prompts the user instead of silently discarding unsaved form changes.
@jasonvarga
Copy link
Copy Markdown
Member

Lookin' ok but don't merge yet. Found a couple issues.

@jackmcdade
Copy link
Copy Markdown
Member Author

By all means!

jasonvarga and others added 2 commits May 1, 2026 16:21
Container.vue now removes its dirty entry in onUnmounted, which fixes the
stale-state leak that caused subsequent link clicks to prompt for a form
that no longer exists after a silent popstate-induced page swap.

dirty-state.js captures the dirty URL/state on the 0->1 add() transition
instead of via a global inertia:navigate listener, removing module-load
state and an event listener.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jasonvarga jasonvarga changed the title [6.x] Warn on dirty state when using browser back/forward Dirty state fixes May 1, 2026
@jasonvarga jasonvarga changed the title Dirty state fixes [6.x] Dirty state fixes May 1, 2026
@jasonvarga jasonvarga merged commit daa2585 into 6.x May 1, 2026
18 of 19 checks passed
@jasonvarga jasonvarga deleted the catch-browser-back-button branch May 1, 2026 20:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No dirty state warning when using browser back button

2 participants