The scenario
We have a settings page. Saving the form redirects back with a "Settings saved." flash. Further down, a "Delete account…" button arms a two-step confirmation: click it and it's replaced by "Yes, delete" and "Cancel".
The burn
Save the form, click "Delete account…", visit another page, then press Back. The stale flash is there again, and so is the delete confirmation, armed, with "Yes, delete" one stray click away.
When we leave a page, Turbo Drive takes a snapshot of it. It uses that snapshot for Back and Forward, and as an instant preview when we link to the page again. The snapshot is a faithful copy of the page exactly as we left it, and nobody tidies up for it unless we ask. Nobody wants a one-time notice or a half-finished dangerous action restored, but that's what we get.
The fix
Two small fixes, of two different shapes.
The flash is server-rendered, one-time markup. In the refactored show.html.erb it carries data-turbo-temporary, and Turbo removes the element before it takes the snapshot. No JavaScript at all.
The confirmation is state the user created in the browser, so the controller that created it cleans it up. confirm_controller.js is the same controller as delete_account_controller.js with one addition: disconnect() calls disarm(). Turbo takes its snapshot after controllers have disconnected, so the cached copy is always disarmed.
When neither fits, there are two more tools. A turbo:before-cache listener can prepare the page for caching when the state isn't owned by a Stimulus controller. And a page too sensitive to cache at all can opt out with a turbo-cache-control meta tag set to no-preview or no-cache.
Why it matters
Any temporary UI is a candidate for this: flashes, confirmations, open menus and modals, revealed secrets, spinners. The question to ask of each is whether it makes sense on a page someone returns to. If not, it should be gone before the snapshot is taken.
It's the same rule as the teardown in disconnect() for a third-party widget: leave the page as you found it. Turbo's cache rewards controllers that clean up after themselves and punishes ones that don't, usually on the Back button, where it's hardest to notice in testing.
Read More
- Turbo Handbook: Preparing the page to be cached, on
data-turbo-temporaryandturbo:before-cache. - Turbo Handbook: Opting out of caching, for
turbo-cache-control.