Keep page state across navigation
You'll build a page with a notes field, a packing checklist and a list of hikes. Open a hike, press Back and the page comes back the way you left it: the notes still typed, the items still ticked and the list still scrolled. It takes one option on the navigator you already have.
Before you start
Install from crates.io. You need no Node and no package manager.
cargo install snapfire_compiler
cargo install snapfire_fsr_cli
fsr --version
Every command and screenshot on this page was captured with fsr 0.x.
A page worth coming back to#
The hikes, in app/src/hikes.ts:
;
;A packing checklist, as an ordinary React component with its own state:
;
;
The index has a plain textarea, the checklist as an island and a link to each hike:
;
;
;
;
;
Each hike gets a short page of its own:
;
;
;
;
;
Some layout, at the end of app/styles/app.css. The tall rows make the list long enough to scroll:
}
}
}
}Without activity#
Open http://127.0.0.1:3000. Type something into Notes, tick Water and Map, scroll down and click Taft Point. Now press Back. The page is there again, but Notes is empty, nothing is ticked and the window is at the top. The navigator rendered the index again from its payload, so whatever the visitor did to the old one is gone.
Turn activity on#
Give the navigator one option in app/src/main.ts:
;
;
;
;
;Do the same thing again: type a note, tick two items, scroll down, open a hike and press Back. The notes are still typed, Packed reads 2 of 5 and the list is scrolled where you left it. Clicking All hikes instead of Back brings back the notes and the ticks too. A link scrolls to the top as any link does, while Back returns to where you were.
- Mist Falls8 mi, 800 ft
- Half Dome16 mi, 4800 ft
- Clouds Rest14 mi, 3100 ft
- Taft Point2 mi, 250 ft
This site's navigator runs with activity: true. Type a note, tick two items, open a hike and press Back: this page comes back as you left it.
What happened#
With activity: true, the navigator stops throwing away the page a visitor leaves. It takes the page's DOM out of the document and keeps it, up to ten pages, the one left longest ago dropped first. When a navigation lands on a page it is keeping, it puts that DOM back and patches it to the new payload, leaving every field's value alone. The textarea keeps its text because it is the same element. A video keeps its position and a custom element keeps whatever it holds for the same reason.
The checklist is a React island. React 19.2 and later can pause a tree without unmounting it. The navigator renders the island under React's <Activity> and switches it to hidden while the page is kept, so its useState survives and its effects stop until the page comes back. A Vue island, or a React island on an earlier React, cannot be paused that way. It is unmounted when the page is kept and mounts afresh when the page comes back.
An action that revalidates, a refresh or a new session drops every kept page, so a visitor never comes back to a page that shows data from before they changed it.
Try it#
Opt one link out. Change the hike links on the index to:
Open a hike from the list and press Back: the index renders fresh, as it did without activity. The attribute works the other way too. Leave enableNavigation() with no options and put data-sf-activity on just the links whose page is worth keeping. activityLimit changes how many pages are kept:
;