Open Hooks

useLocalStorage

Persist state in localStorage to keep data between page reloads in React apps.

useLocalStorage

A custom React hook to synchronize a stateful value with localStorage. It enables persistent state by saving the value to browser storage and retrieving it on page load. Ideal for user preferences, theme settings, or any data you want to keep between sessions.


📦 Installation

Works out of the box with React and Next.js projects. No external dependencies required.

No external dependency needed

🧠 Purpose

\useLocalStorage\ simplifies persistent state management by abstracting localStorage read/write operations and syncing with React state.


💡 Use Cases

  • Persist user preferences (dark mode, language)
  • Keep form data on page reloads
  • Cache temporary data between sessions
  • Store tokens or auth flags (with caution)

📁 Hook Code

useLocalStorage.ts
import { useState, useEffect } from "react";

export function useLocalStorage<T>(key: string, initialValue: T) {
  const [storedValue, setStoredValue] = useState<T>(() => {
    if (typeof window === "undefined") return initialValue;
    try {
      const item = window.localStorage.getItem(key);
      return item ? (JSON.parse(item) as T) : initialValue;
    } catch (error) {
      console.warn("useLocalStorage: error reading key", key, 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.warn("useLocalStorage: error setting key", key, error);
    }
  };

  return [storedValue, setValue] as const;
}
useLocalStorage.js
import { useState, useEffect } from "react";

export function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    if (typeof window === "undefined") return initialValue;
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.warn("useLocalStorage: error reading key", key, error);
      return initialValue;
    }
  });

  const setValue = (value) => {
    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.warn("useLocalStorage: error setting key", key, error);
    }
  };

  return [storedValue, setValue];
}

🧪 Example

import React from "react";
import { useLocalStorage } from "@/hooks/useLocalStorage";

export default function DarkModeToggle() {
  const [darkMode, setDarkMode] = useLocalStorage<boolean>("darkMode", false);

  return (
    <button onClick={() => setDarkMode((prev) => !prev)}>
      {darkMode ? "Switch to Light Mode" : "Switch to Dark Mode"}
    </button>
  );
}
import React from "react";
import { useLocalStorage } from "@/hooks/useLocalStorage";

function DarkModeToggle() {
  const [darkMode, setDarkMode] = useLocalStorage("darkMode", false);

  return (
    <button onClick={() => setDarkMode(prev => !prev)}>
      {darkMode ? "Switch to Light Mode" : "Switch to Dark Mode"}
    </button>
  );
}

export default DarkModeToggle;

🧩 Hook Signature

function useLocalStorage<T>(
  key: string,
  initialValue: T
): readonly [T, (value: T | ((val: T) => T)) => void];

📝 Parameters

PropTypeDefault
initialValue
T
-
key
string
-

🎯 Returns

PropTypeDefault
setValue
(value: T | (val: T) => T) => void
-
storedValue
T
-

🧯 How It Works

LocalStorage Sync

On initialization, it tries to read the value from localStorage. On updates, it writes the new value back to localStorage, keeping React state and browser storage in sync.


🏁 Summary

  • ✅ Persistent state synced with localStorage
  • ✅ Supports functional updates
  • ✅ Works with any serializable value
  • ✅ TypeScript friendly with generics

🙋 Contribution

Found a bug or improvement? Submit a PR on GitHub