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

@rm-hull/use-local-storage

v0.3.4

Published

A React hook for syncing state with localStorage

Downloads

483

Readme

use-local-storage

NPM Version Coveralls NPM Downloads

A type-safe React hook for syncing state with localStorage that automatically keeps your data in sync across browser tabs and handles SSR gracefully. Built with Jotai for efficient state management.

Why use this instead of react-use's useLocalStorage?

Unlike react-use, this library provides:

  • True cross-tab synchronization - When you update localStorage in one component/tab, all other instances automatically re-render with the new value, even within the same tab
  • Consistent loading states - The isLoading flag helps you handle hydration correctly in SSR scenarios
  • Shared state management - Multiple components using the same key share a single atom, preventing unnecessary re-renders
  • Simpler API - Returns a single object with value, setValue, and isLoading instead of a tuple

Quick Start

npm install @rm-hull/use-local-storage
# or
yarn add @rm-hull/use-local-storage

Usage Examples

Basic Counter

import { useLocalStorage } from "@rm-hull/use-local-storage";

function Counter() {
  const { value = 0, setValue, isLoading, error } = useLocalStorage<number>("counter");

  if (isLoading) return <div>Loading...</div>;
  if (!!error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <p>Count: {value}</p>
      <button onClick={() => setValue(value + 1)}>Increment</button>
      <button onClick={() => setValue(undefined)}>Clear</button>
    </div>
  );
}

User Preferences

interface UserPreferences {
  theme: "light" | "dark";
  notifications: boolean;
}

function Settings() {
  const { value: prefs, setValue } =
    useLocalStorage<UserPreferences>("user-prefs");

  return (
    <div>
      <label>
        <input
          type="checkbox"
          checked={prefs?.theme === "dark"}
          onChange={(e) =>
            setValue({
              ...prefs,
              theme: e.target.checked ? "dark" : "light",
            })
          }
        />
        Dark Mode
      </label>
    </div>
  );
}

Shopping Cart

interface CartItem {
  id: string;
  name: string;
  quantity: number;
}

function ShoppingCart() {
  const { value: cart = [], setValue } = useLocalStorage<CartItem[]>("cart");

  const addItem = (item: CartItem) => {
    setValue([...cart, item]);
  };

  const removeItem = (id: string) => {
    setValue(cart.filter((item) => item.id !== id));
  };

  return (
    <div>
      <h2>Cart ({cart.length} items)</h2>
      {cart.map((item) => (
        <div key={item.id}>
          {item.name} x {item.quantity}
          <button onClick={() => removeItem(item.id)}>Remove</button>
        </div>
      ))}
    </div>
  );
}

Custom Serializer

You can provide a custom serializer to transform the data before it's stored and after it's retrieved. This is useful for encrypting data or transforming it in other ways.

A silly example that reverses the string before writing it to localStorage:

const reverseSerializer = {
  serialize: (value: string) => value.split('').reverse().join(''),
  deserialize: (value: string) => value.split('').reverse().join(''),
};

const { value } = useLocalStorage('my-key', { serializer: reverseSerializer });

A more practical use case would be to use a library like crypto-js to encrypt the data before storing it.

Features

  • Auto-sync across tabs - Changes in one tab are instantly reflected in others
  • Type-safe - Full TypeScript support with generics
  • SSR-compatible - Handles server-side rendering gracefully
  • Efficient - Uses Jotai for optimized re-renders
  • Easy cleanup - Pass undefined to remove items
  • Lightweight - Minimal bundle size

Contributer Guidelines

Releasing a New Version

This project uses Changesets to manage versioning and automated npm publishing.

How the release process works

  1. Create a changeset on your feature branch

    When you’ve made changes you want to release, first create a new branch (not on main):

    git checkout -b feature/my-change

    Make your changes, then run:

    yarn changeset

    You’ll be prompted to:

    • Choose the type of version bump (patch, minor, or major)
    • Write a short summary of the change

    This command creates a markdown file in the .changeset/ directory describing your upcoming release.

  2. Commit and push your feature branch

    git add .changeset
    git commit -m "Add changeset for upcoming release"
    git push -u origin feature/my-change
  3. Merge the feature branch into main

    • Once your PR is reviewed, merge it into main. The .changeset file must be present in main for the next step.
  4. Automatic version bump and publish

    • When changes are pushed to main, GitHub Actions will automatically:

      • Build the package
      • Apply version bumps based on the .changeset file
      • Update the changelog
      • Publish the new version to npm as @rm-hull/use-local-storage
  5. That's it! Your package is now live on npm with an updated version and changelog.


Notes

  • The npm publish step is automated via GitHub Actions (.github/workflows/release.yml).
  • Ensure your NPM_TOKEN secret is configured in the repository settings under Settings → Secrets → Actions.
  • Changesets should always be created on feature branches, not directly on main.
  • No pull requests are created for version bumps; merging your feature branch into main triggers the release automatically.