JAVASCRIPT

React useLocalStorage Hook for Persisting State Across Sessions

Learn to build a `useLocalStorage` React hook to easily store and retrieve component state from the browser's local storage, perfect for user preferences or persistent data.

import { useState, useEffect, useCallback } from 'react';

function useLocalStorage(key, initialValue) {
  // State to store our value
  // Pass initial state function to useState so logic is only executed once
  const [storedValue, setStoredValue] = useState(() => {
    try {
      if (typeof window === 'undefined') {
        return initialValue;
      }
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  // Return a wrapped version of useState's setter function that ...
  // ... persists the new value to localStorage.
  const setValue = useCallback((value) => {
    try {
      if (typeof window === 'undefined') {
        console.warn("localStorage is not available.");
        return;
      }
      // Allow value to be a function so we have same API as useState
      const valueToStore =
        value instanceof Function ? value(storedValue) : value;
      // Save state
      setStoredValue(valueToStore);
      // Save to local storage
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error(error);
    }
  }, [key, storedValue]); // Dependency `storedValue` ensures latest state for functional updates

  // Optional: Add an effect to listen for changes from other tabs/windows
  useEffect(() => {
    const handleStorageChange = (event) => {
      if (event.key === key) {
        try {
          setStoredValue(event.newValue ? JSON.parse(event.newValue) : initialValue);
        } catch (error) {
          console.error("Error parsing storage change:", error);
        }
      }
    };

    if (typeof window !== 'undefined') {
      window.addEventListener('storage', handleStorageChange);
      return () => {
        window.removeEventListener('storage', handleStorageChange);
      };
    }
  }, [key, initialValue]);

  return [storedValue, setValue];
}

// Example Usage:
/*
import React from 'react';
import useLocalStorage from './useLocalStorage'; // Assuming useLocalStorage is in its own file

function ThemeSwitcher() {
  const [theme, setTheme] = useLocalStorage('app-theme', 'light');

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

  return (
    <div style={{ background: theme === 'dark' ? '#333' : '#fff', color: theme === 'dark' ? '#fff' : '#333', padding: '20px' }}>
      <h1>Current theme: {theme}</h1>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
}
*/
How it works: The `useLocalStorage` hook initializes state by attempting to retrieve a value from `localStorage` based on a given `key`. If found, it parses and uses that value; otherwise, it uses the `initialValue`. The `setValue` function is a wrapped `setStoredValue` that also serializes and saves the new value to `localStorage`. A `useEffect` hook also listens for `storage` events, allowing changes made in other tabs or windows to synchronize the state in the current component, ensuring consistency across the application.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs