Syncing State with localStorage
Build a useLocalStorage custom hook that keeps React state and localStorage in sync bidirectionally.
Syncing State with localStorage is a free React Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the React Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
The Naive Approach Gets Out of Sync
A first instinct is to read localStorage in one place and write it in another, but these can drift apart. State updates that forget to persist, or persisted values not reflected in state, lead to bugs.
You want a single source of truth that keeps React state and storage in lockstep.
The useLocalStorage Custom Hook
The clean solution is a useLocalStorage hook that wraps useState and persists changes automatically. It returns a value and setter just like useState.
Consumers use it exactly like normal state but get persistence for free.
Lazy Initial Function
Reading localStorage is synchronous work, so you should not do it on every render. Pass a function to useState, the lazy initializer, so the read only happens once on mount.
Inside that function you parse the stored value or return the provided default.
Why Lazy Initialization Matters
If you compute the initial state directly as an argument, that expression runs on every render even though it is ignored after the first. With a function, React calls it only once.
For localStorage reads this avoids repeated synchronous main-thread work each render.
Syncing via useEffect
To persist updates, add a useEffect that runs whenever the value changes and calls localStorage.setItem(key, JSON.stringify(value)).
Putting the value in the dependency array means every state change writes through to storage automatically.
Functional Updates Avoid Stale Closures
When setting based on the previous value, use the functional form setValue(prev => prev + 1). This avoids stale closures where an old captured value would be written.
It is especially important inside event handlers and effects that may capture an outdated snapshot.
Handling a Null Stored Value
getItem returns null when the key does not exist. Your initializer must detect this and fall back to the default instead of trying to parse null.
A guard like stored != null ? JSON.parse(stored) : defaultValue handles the first visit gracefully.
TypeScript Generics
In TypeScript you make the hook generic: function useLocalStorage<T>(key: string, initial: T). This preserves the value type so consumers get full type safety.
The returned tuple is typed as [T, Dispatch<SetStateAction<T>>], matching useState.
Versioning Stored Data
Over time your data shape changes, and old stored values can break new code. Store a version key alongside the data so you can detect outdated formats.
For example save { version: 2, data } and compare the version on read.
Purging on Version Mismatch
When the stored version does not match the current one, discard or migrate the old value instead of trusting it. The simplest strategy is to remove the stale entry and fall back to defaults.
This prevents crashes from data written by an older release of your app.
Putting the Hook Together
A complete useLocalStorage combines a lazy initializer that reads and parses safely, a setter that supports functional updates, and a useEffect that writes through with versioning.
The result is a drop-in replacement for useState that survives reloads.
Quick Check
Test your understanding of state initialization.
Recap
You learned to build a useLocalStorage hook with a lazy initializer, persist changes via useEffect, use functional updates to avoid stale closures, and handle null values.
You also saw TypeScript generics and versioning to purge stale data safely.
Frequently asked questions
Is the “Syncing State with localStorage” lesson free?
Yes — the full text of “Syncing State with localStorage” is free to read here on the web, and the React Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the React Academy course, upgrade to CoddyKit PRO.
What will I learn in “Syncing State with localStorage”?
Build a useLocalStorage custom hook that keeps React state and localStorage in sync bidirectionally. You practise React Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start React Academy?
No prior experience is required. React Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Syncing State with localStorage” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this React Academy lesson?
Yes. Every React Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.