npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@akb2/react-use-local-storage

v1.1.1

Published

A state hook with browser's localStorage

Readme

react-use-local-storage

A React state hook backed by localStorage, built on useSyncExternalStore. Read and update values by key, subscribe to changes, and access storage outside React components.

Installation

pnpm add react-use-local-storage

Declared peer dependencies: react@^18.3.1 and @akb2/types-tools@^1.3.0. The current React peer dependency range does not include React 19.

The package provides ESM, CommonJS, and TypeScript declarations.

Quick start

import { useLocalStorageState } from "react-use-local-storage";

export const Counter = () => {
  const [count, setCount] = useLocalStorageState<number>("counter", 0);

  return (
    <button onClick={() => setCount(count + 1)}>
      Clicks: {count}
    </button>
  );
};

The optional second argument is a fallback for a missing key. Without a fallback, a missing key returns null. The fallback is used for reading and rendering only; it is not written to storage. Removing the key makes the hook return its fallback again.

API

useLocalStorageState<T>(key, fallback?)

const [value, setValue, storageKey] = useLocalStorageState<string>("name", "Guest");

| Element | Description | | --- | --- | | value | The stored value, or the fallback for a missing key (null by default) | | setValue | Writes a new value; accepts nullable values for removal | | storageKey | The key passed to the hook |

setValue("Andrew");
setValue(`${value}!`);
setValue(null); // Removes the key; this hook returns "Guest" again.

The hook's setter is typed as Dispatch<Nullable<T>>: pass a value directly. Functional updater callbacks are not part of this public hook signature.

A non-null fallback selects an overload with a non-null return type. This is a TypeScript declaration, not runtime validation of existing storage data.

For object or array fallbacks, reuse a stable reference so that repeated snapshot reads return the same value when the key is missing:

const DEFAULT_PREFERENCES = { theme: "light" as const };

// Inside a component:
const [preferences, setPreferences] = useLocalStorageState<{
  theme: "light" | "dark";
}>("preferences", DEFAULT_PREFERENCES);

Avoid passing a newly created object or array as the fallback on every render, especially during server rendering and hydration.

getLocalStorageValue<T>(key, fallback?)

Reads a value without creating a React subscription:

import { getLocalStorageValue } from "react-use-local-storage";

const name = getLocalStorageValue<string>("name", "Guest");
const missing = getLocalStorageValue<string>("missing"); // null if absent

Returns the fallback when the key is missing or window is unavailable. An omitted, null, or undefined fallback is normalized to null by the getter. The generic type T describes the expected value; it does not validate stored data at runtime.

setLocalStorageValue<T>(key, value)

Writes a value and notifies subscribers to that key in the current window:

import { setLocalStorageValue } from "react-use-local-storage";

setLocalStorageValue("name", "Andrew");
setLocalStorageValue<number>("counter", 1);

Writing the same serialized content does not notify subscribers again. Passing null removes the value.

removeLocalStorageValue(key)

Removes a value through the library's setter and notifies subscribers if the stored value changes:

import { removeLocalStorageValue } from "react-use-local-storage";

removeLocalStorageValue("name");

clearLocalStorage()

import { clearLocalStorage } from "react-use-local-storage";

clearLocalStorage();

Clears all localStorage entries for the current origin, including keys written by other code. It also clears the internal cache and notifies subscribers to keys that existed before the operation.

Storage format

Values are stored in a JSON wrapper with a value property:

setLocalStorageValue("name", "Andrew");

localStorage.getItem("name");
// '{"value":"Andrew"}'

Use JSON-compatible data. For example, Date becomes a string during serialization; circular objects and BigInt cause serialization errors.

Read values are cached by storage key and raw content. Parsed data is passed through deepFreeze. Create new values when updating objects:

import { useLocalStorageState } from "react-use-local-storage";

type Preferences = {
  theme: "light" | "dark";
};

export const ThemeButton = () => {
  const [preferences, setPreferences] =
    useLocalStorageState<Preferences>("preferences");

  return (
    <button
      onClick={() =>
        setPreferences({ ...preferences, theme: "dark" })
      }
    >
      Theme: {preferences?.theme ?? "light"}
    </button>
  );
};

When reading data written by other code, the getter reads the .value property of parsed JSON. If parsing or subsequent processing throws, it returns the original nonempty string. Arbitrary JSON without the wrapper is not the library's storage format.

The fallback does not replace all invalid stored data:

| Stored content | Getter result | | --- | --- | | Missing key | Fallback, or null by default | | {"value":42} | 42 | | {} | undefined, even with a fallback | | {"value":null} | null, even with a non-null fallback | | Nonempty invalid JSON | The original raw string | | Empty string | Fallback |

The getter currently returns parsedData directly after reading .value. Therefore, the non-null fallback overload does not guarantee a non-null runtime result for arbitrary existing data.

Synchronization

  • In the current window, use the hook's setter or the library utilities to notify subscribers.
  • External changes are handled through storage events for localStorage. The handler supports key updates, removal, and storage clearing.
  • Direct calls to localStorage.setItem(), removeItem(), or clear() do not fire a storage event in the window that made the change. This subscription does not automatically detect those local writes.
  • Cross-tab synchronization requires the same origin: protocol, host, and port.

When writing directly from another tab, use the JSON.stringify({ value: ... }) format.

Server-side rendering

The hook's server snapshot is its fallback (null by default). When window is unavailable, the getter returns its fallback, and writes and clearing are no-ops. During hydration, use the same fallback on the server and client. After hydration, React uses the client snapshot from localStorage.

The fallback does not initialize or overwrite stored data.

Limitations

  • Accessing or writing to localStorage can throw, for example when access is denied or the storage quota is exceeded. The library does not catch these errors.
  • Updates to multiple keys are not transactional.
  • Stored data is not validated against the declared TypeScript type.

Development

pnpm install
pnpm test
pnpm run test:watch
pnpm build

Build output is written to .dist: index.js for ESM, index.cjs for CommonJS, and type declarations.

Additional commands:

pnpm lint
pnpm run lint:fix
pnpm format
pnpm run format:check

For package maintainers, publish with:

pnpm release

This command builds the package and then publishes it with public access.

Links

License

MPL-2.0. Author: akb2.