This is a hook for persisting state to localStorage. Update the error handling to suit your needs.

import { useCallback, useState } from "react";

type UpdaterFn<TState> = (currentState: TState) => TState;

type Result<TState> = [
  state: TState,
  wrappedSetState: (update: TState | UpdaterFn<TState>) => void

export default function useLocalStorage<TState>(
  key: string,
  initialValue: TState
): Result<TState> {
  const [state, setState] = useState<TState>(() => {
    try {
      const item = window.localStorage.getItem(key);

      return item ? JSON.parse(item) : initialValue;
    } catch (error) {

      return initialValue;

  const wrappedSetState = useCallback(
    (update) => {
      try {
        setState((currentState) => {
          const nextState =
            typeof update === "function" ? update(currentState) : update;

          window.localStorage.setItem(key, JSON.stringify(nextState));
          return nextState;
      } catch (error) {

  return [state, wrappedSetState];

Let's talk some more about JavaScript, React, and software engineering.

I write a newsletter to share my thoughts and the projects I'm working on. I would love for you to join the conversation. You can unsubscribe at any time.

Introduction to State Machines and XState Logo
Introduction to State Machines and XState

Check out my courses!

Liked the post? You might like my video courses, too. Click the button to view this course or go to Courses for more information.
View on