Code Snippets
/

React useLocalStorage Hook

React useLocalStorage Hook

Persisting a piece of state to localStorage so it survives a page reload is one of the most-requested custom hooks. This snippet covers the basic JSON-serialised hook, an SSR-safe variant that lazily reads only on the client, and a cross-tab sync version that listens for the storage event. Pick the one that matches whether your app is client-only, server-rendered, or multi-window.

JavaScript
Easy
3 snippets
react
hooks
js-local-storage
code-template

496 views

4

function useLocalStorage(key, initialValue) {
    const [value, setValue] = useState(() => {
        try {
            const raw = localStorage.getItem(key);
            return raw !== null ? JSON.parse(raw) : initialValue;
        } catch {
            return initialValue;
        }
    });
    const update = (next) => {
        const resolved = typeof next === 'function' ? next(value) : next;
        setValue(resolved);
        try { localStorage.setItem(key, JSON.stringify(resolved)); } catch {}
    };
    return [value, update];
}

// Smoke-test: a tiny mock that respects lazy initializers.
function useState(init) {
    const v = typeof init === 'function' ? init() : init;
    return [v, () => {}];
}
const [theme, setTheme] = useLocalStorage('theme', 'light');
console.log('theme:', theme);

The lazy initializer (() => ...) reads from localStorage only once, which avoids parsing JSON on every render. The setter accepts either a value or a functional updater, mirroring the setState API so callers can keep using setTheme((t) => t === 'light' ? 'dark' : 'light'). Wrapping reads and writes in try/catch handles two real failures: quota exceeded on the write side, and corrupt JSON or disabled storage on the read side. This is the right shape for a single-tab, client-only app.