Skip to main content

Overview

The useLocalStorage hook provides a simple way to persist state in the browser’s localStorage. It automatically syncs state changes to localStorage and retrieves stored values on mount, with built-in error handling for cases where localStorage is unavailable.

Signature

const useLocalStorage = <T>(key: string, initialValue: T): readonly [T, React.Dispatch<React.SetStateAction<T>>]

Parameters

key
string
required
The localStorage key to store the value under
initialValue
T
required
The initial value to use if no value exists in localStorage

Return Value

Returns a tuple containing:
  • storedValue (T): The current value stored in localStorage
  • setStoredValue: A setter function to update the stored value (same signature as React’s useState)

Usage

Basic Example

import { useLocalStorage } from './hooks/use-local-storage';

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

  return (
    <div>
      <p>Current theme: {theme}</p>
      <button onClick={() => setTheme('dark')}>
        Switch to Dark
      </button>
    </div>
  );
}

Storing Complex Objects

import { useLocalStorage } from './hooks/use-local-storage';

interface UserSettings {
  notifications: boolean;
  language: string;
  fontSize: number;
}

function SettingsPanel() {
  const [settings, setSettings] = useLocalStorage<UserSettings>('user-settings', {
    notifications: true,
    language: 'en',
    fontSize: 16,
  });

  const toggleNotifications = () => {
    setSettings(prev => ({
      ...prev,
      notifications: !prev.notifications,
    }));
  };

  return (
    <div>
      <label>
        <input
          type="checkbox"
          checked={settings.notifications}
          onChange={toggleNotifications}
        />
        Enable notifications
      </label>
    </div>
  );
}

Array Storage

import { useLocalStorage } from './hooks/use-local-storage';

function TodoList() {
  const [todos, setTodos] = useLocalStorage<string[]>('todos', []);

  const addTodo = (todo: string) => {
    setTodos(prev => [...prev, todo]);
  };

  const removeTodo = (index: number) => {
    setTodos(prev => prev.filter((_, i) => i !== index));
  };

  return (
    <div>
      <ul>
        {todos.map((todo, i) => (
          <li key={i}>
            {todo}
            <button onClick={() => removeTodo(i)}>Remove</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

Use Cases

  • User Preferences: Store theme settings, language preferences, or UI customizations
  • Form State: Persist form data to prevent loss on page refresh
  • Shopping Carts: Maintain cart state across sessions
  • Feature Flags: Store user-specific feature toggles
  • Recently Viewed: Track recently accessed items or pages

Implementation Details

  • Values are automatically serialized to JSON when stored and deserialized when retrieved
  • Changes to the stored value trigger a useEffect that updates localStorage
  • Error handling is built-in with console warnings for localStorage failures
  • Uses the useWindow hook for safe window access
  • Initial value is only used if no stored value exists or if parsing fails

Error Handling

The hook gracefully handles cases where:
  • localStorage is unavailable (e.g., in private browsing mode)
  • The stored value cannot be parsed as JSON
  • Setting localStorage fails due to quota exceeded
In all error cases, the hook logs a warning to the console and falls back to the initial value.

Build docs developers (and LLMs) love