dotUI
dotUI
beta
  1. Hooks
  2. Browser hooks
  3. useLocalStorage

useLocalStorage

Store, retrieve, and synchronize data from the browser’s localStorage API with useLocalStorage

"use client";

import * as React from "react";
import { Button } from "@/lib/components/core/default/button";
import { TextArea } from "@/lib/components/core/default/text-area";
import { ClientOnly } from "@/lib/components/utils/client-only";
import { useLocalStorage } from "@/lib/hooks/use-local-storage";

function Demo() {
  const [writing, saveWriting] = useLocalStorage<string | null>("writing", null);
  const [input, setInput] = React.useState(writing ?? "");

  return (
    <div className="w-full max-w-sm">
      <TextArea
        value={input}
        onChange={(value) => {
          setInput(value);
        }}
        placeholder="Start your writing here, save it and refresh the page to see it persist."
      />
      <div className="mt-4 flex items-center justify-end space-x-2">
        <Button
          size="sm"
          onPress={() => {
            setInput("");
            saveWriting(null);
          }}
        >
          Clear
        </Button>
        <Button
          variant="primary"
          size="sm"
          onPress={() => {
            saveWriting(input);
          }}
        >
          Save
        </Button>
      </div>
    </div>
  );
}

function ClientOnlyDemo() {
  return (
    <ClientOnly>
      <Demo />
    </ClientOnly>
  );
}

Installation

Copy and paste the following code into your project.

import React from "react";

function dispatchStorageEvent(key: string, newValue: string | null) {
  window.dispatchEvent(new StorageEvent("storage", { key, newValue }));
}

const setLocalStorageItem = <T>(key: string, value: T) => {
  const stringifiedValue = JSON.stringify(value);
  window.localStorage.setItem(key, stringifiedValue);
  dispatchStorageEvent(key, stringifiedValue);
};

const removeLocalStorageItem = (key: string) => {
  window.localStorage.removeItem(key);
  dispatchStorageEvent(key, null);
};

const getLocalStorageItem = (key: string) => {
  return window.localStorage.getItem(key);
};

const useLocalStorageSubscribe = (callback: (event: StorageEvent) => void) => {
  window.addEventListener("storage", callback);
  return () => window.removeEventListener("storage", callback);
};

const getLocalStorageServerSnapshot = () => {
  throw Error("useLocalStorage is a client-only hook");
};

export function useLocalStorage<T>(
  key: string,
  initialValue: T
): [T, React.Dispatch<React.SetStateAction<T>>] {
  const getSnapshot = () => getLocalStorageItem(key);

  const store = React.useSyncExternalStore(
    useLocalStorageSubscribe,
    getSnapshot,
    getLocalStorageServerSnapshot
  );

  const setState: React.Dispatch<React.SetStateAction<T>> = React.useCallback(
    (v: T | ((prevState: T) => T)) => {
      try {
        const nextState =
          typeof v === "function" ? (v as (prevState: T) => T)(JSON.parse(store!) as T) : v;

        if (nextState === undefined || nextState === null) {
          removeLocalStorageItem(key);
        } else {
          setLocalStorageItem(key, nextState);
        }
      } catch (e) {
        console.warn(e);
      }
    },
    [key, store]
  );

  React.useEffect(() => {
    if (getLocalStorageItem(key) === null && typeof initialValue !== "undefined") {
      setLocalStorageItem(key, initialValue);
    }
  }, [key, initialValue]);

  return [store ? (JSON.parse(store) as T) : initialValue, setState];
}

Update the import paths to match your project setup.

Parameters

NameTypeDescription
key
string
The key used to access the local storage value.
initialValue
any
The initial value to use if there is no item in the local storage with the provided key.

Return values

NameTypeDescription
localState
any
The current state of the value stored in local storage.
handleSetState
function
A function to set the state of the value in the local storage. This function accepts a new value or a function that returns a new value. The value is also saved in the local storage under the provided key.

Built by mehdibha. The source code is available on GitHub.