avatar

Le Do Nghiem

Software Engineer

  • About me
  • Books
  • Snippets
  • Blog

© 2026 Le Do Nghiem. All rights reserved.

Contact |

Back to Snippets

useLocalStorage Hook

A React hook to persist state in localStorage with automatic synchronization.

LanguageTypeScript
Last UpdatedApr 25, 2025
use-local-storage.ts
import { useState, useEffect } from 'react';

export function useLocalStorage<T>(
  key: string,
  initialValue: T
): [T, (value: T | ((val: T) => T)) => void] {
  const [storedValue, setStoredValue] = useState<T>(() => {
    if (typeof window === 'undefined') {
      return initialValue;
    }
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  const setValue = (value: T | ((val: T) => T)) => {
    try {
      const valueToStore =
        value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      if (typeof window !== 'undefined') {
        window.localStorage.setItem(key, JSON.stringify(valueToStore));
      }
    } catch (error) {
      console.error(error);
    }
  };

  return [storedValue, setValue];
}

Persisting state to localStorage is a common need — for user preferences, form drafts, or theme settings. This hook makes it easy to sync React state with localStorage.

import { useState, useEffect } from 'react';

export function useLocalStorage<T>(
  key: string,
  initialValue: T
): [T, (value: T | ((val: T) => T)) => void] {
  const [storedValue, setStoredValue] = useState<T>(() => {
    if (typeof window === 'undefined') {
      return initialValue;
    }
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  const setValue = (value: T | ((val: T) => T)) => {
    try {
      const valueToStore =
        value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      if (typeof window !== 'undefined') {
        window.localStorage.setItem(key, JSON.stringify(valueToStore));
      }
    } catch (error) {
      console.error(error);
    }
  };

  return [storedValue, setValue];
}

How It Works

  • On mount, reads from localStorage and initializes state.
  • Updates both state and localStorage when value changes.
  • Handles SSR by checking for window.
  • Supports functional updates like useState.

Example Usage

function ThemeToggle() {
  const [theme, setTheme] = useLocalStorage('theme', 'light');

  const toggleTheme = () => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light');
  };

  return (
    <button onClick={toggleTheme}>
      Current theme: {theme}
    </button>
  );
}

More Examples

// Form draft persistence
const [draft, setDraft] = useLocalStorage('form-draft', '');

// User preferences
const [preferences, setPreferences] = useLocalStorage('user-prefs', {
  notifications: true,
  language: 'en'
});

Use Cases

  • Theme preferences (dark/light mode)
  • Form draft saving
  • User settings
  • Shopping cart persistence
  • Recently viewed items

Notes

  • Only works with JSON-serializable values.
  • Handles SSR gracefully by checking for window.
  • Automatically syncs across tabs (localStorage is shared).
  • For complex objects, ensure they're JSON-serializable.

A simple hook that bridges React state and browser storage, making persistence effortless.

Previous Snippet

useToggle Hook

Next Snippet

useClickOutside Hook