javascript 49 lines · 9 steps

Versioned state migrations in localStorage

A persistence layer that upgrades old saved state through numbered migration steps before handing it to the app.

Explained by highlit
1const STORAGE_KEY = "app_state";
2const CURRENT_VERSION = 3;
3 
4const migrations = {
5 1: (state) => ({ ...state, theme: state.theme ?? "light" }),
6 2: (state) => ({ ...state, filters: { query: state.search ?? "", tags: [] }, search: undefined }),
7 3: (state) => ({ ...state, sidebar: { collapsed: false, width: 240 } }),
8};
9 
10function migrate(state, fromVersion) {
11 let migrated = state;
12 for (let v = fromVersion + 1; v <= CURRENT_VERSION; v++) {
13 const step = migrations[v];
14 if (step) migrated = step(migrated);
15 }
16 return migrated;
17}
18 
19export function loadState(defaults) {
20 const raw = localStorage.getItem(STORAGE_KEY);
21 if (!raw) return { ...defaults };
22 
23 try {
24 const { version = 0, data } = JSON.parse(raw);
25 if (version === CURRENT_VERSION) return { ...defaults, ...data };
26 
27 const upgraded = migrate(data, version);
28 const state = { ...defaults, ...upgraded };
29 saveState(state);
30 return state;
31 } catch (err) {
32 console.warn("Corrupt persisted state, resetting", err);
33 localStorage.removeItem(STORAGE_KEY);
34 return { ...defaults };
35 }
36}
37 
38export function saveState(data) {
39 const payload = JSON.stringify({ version: CURRENT_VERSION, data });
40 try {
41 localStorage.setItem(STORAGE_KEY, payload);
42 } catch (err) {
43 if (err.name === "QuotaExceededError") {
44 console.error("localStorage quota exceeded, state not saved");
45 } else {
46 throw err;
47 }
48 }
49}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Tagging persisted data with a version number lets you evolve its shape without breaking existing users.
  2. 2Chaining small pure migration functions keeps each schema change isolated and testable.
  3. 3Persistence code should treat stored data as untrusted and degrade gracefully to defaults on corruption.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Versioned state migrations in localStorage — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code