SvelteKit snapshot: Client-Side Scroll & Input Restoration
- Last updated
- Prerequisites:
- SvelteKit navigation and page lifecycle
- Understanding of SPA back-navigation behavior
- sveltekit
- snapshot
- restore
- scroll
- form-state
- navigation
Read at your depth
The practical view
SvelteKit's snapshot feature preserves page state across navigations. In a component (usually +page.svelte or +layout.svelte), export a snapshot object: export const snapshot = { capture: () => ({ query: searchTerm }), restore: (value) => { searchTerm = value; } };. When you navigate away and then back (browser back/forward), the captured value is restored. This is ideal for search inputs, filter states, and scroll positions that should survive back-navigation. SvelteKit also preserves scroll position automatically for simple cases; snapshots extend this to arbitrary component state. The captured value must be JSON-serializable (no functions, no DOM nodes).
The same idea in other frameworks
React Router's <ScrollRestoration> and component state keyed by location cover similar ground.Angular's Router scroll restoration + route state is the analogue.Vue Router's scrollBehavior and keep-alive state play the same role.SvelteKit snapshots — the current topic.Legacy vs modern
Lost form state on back vs snapshot restoration
Without snapshots, navigating away and back clears the search input; the snapshot capture/restore pair rebuilds it exactly.
let query = $state(''); // ❌ OLD: navigating away and back resets thisexport const snapshot = {
capture: () => ({ query, results }), // ✅ NEW
restore: (value) => { query = value.query; results = value.results; }, // ✅ NEW
};Interview gotchas
Context
This probes the constraints and edge cases — interviewers want to know the serialization limit and the hard-refresh/new-tab behavior, which determines whether snapshots are a safe storage mechanism.
The mechanical answer
Snapshots must be JSON-serializable: plain objects, arrays, strings, numbers, booleans, null, and Date (via the structured-clone-compatible history state). You cannot store functions, class instances, DOM nodes, Maps, or cyclic structures. Because the state lives in the browser's history entry, it is only restored on back/forward navigation within the same tab — a hard refresh (F5), opening the URL in a new tab, or sharing the link does NOT restore snapshots (the history state is gone). For those cases you need URL-driven state or persistence (localStorage/sessionStorage). Also, restore runs only if the component with the snapshot export is still the matching page; structural changes to the tree can orphan saved snapshots.
Trap
The naive answer is 'snapshots survive refresh'. They live in history state and do not survive a full page reload. Another trap: trying to save non-serializable values (functions, DOM refs) — the state must be structured-cloneable and will fail or be stripped. Candidates who describe the history-state scoping and the serialization constraint show real understanding.
Context
This tests the division of labor between framework-managed scroll and app-managed state — interviewers want to know what SvelteKit does automatically and what snapshots add.
The mechanical answer
SvelteKit automatically tracks and restores scroll positions across back/forward navigations: it records the scroll position in the history state and restores it on return, so a long list re-scrolls to where you were without any component code. Snapshots complement this for app-level state that the browser cannot infer — a search input's text, filter selections, form drafts, or the current pagination page. Scroll restoration is about the viewport; snapshots are about the component's reactive state. The two cooperate: on back navigation, the framework restores the scroll offset and then calls restore() on components so both the position and the data-driven UI come back together. You need snapshots whenever the visible state depends on more than the scroll offset.
Trap
The naive answer is 'snapshots replace scroll restoration'. They serve different layers; scroll is handled automatically, snapshots carry app state. Another trap: assuming restore() runs before the DOM is ready — the timing is tied to the page mount, so reading layout in restore may need an effect. Candidates who articulate the scroll-vs-state split show real SvelteKit experience.