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

storage-watcher

v0.1.0

Published

Zero-dependency reactive watcher for localStorage & sessionStorage changes. Detect key mutations, watch future keys, and get notified of new key creation — all within the same tab.

Readme

storage-watcher

A zero-dependency, lightweight npm package that lets you subscribe to localStorage and sessionStorage changes in real-time within the same browser tab.

Normally, there's no way to know when a value in storage changes within the same tab — the browser's native storage event only fires in other tabs. This package solves that by intercepting storage write operations and notifying your callbacks instantly.

Installation

npm install storage-watcher

Quick Start

import { StorageWatcher } from 'storage-watcher';

const watcher = new StorageWatcher();

// Watch a specific key
const unsub = watcher.on('user-token', (newValue, oldValue) => {
  console.log(`Token changed: ${oldValue} → ${newValue}`);
});

// Later, when done watching
unsub();

// Clean up everything
watcher.destroy();

API

new StorageWatcher(options?)

Creates a new watcher instance.

| Option | Type | Default | Description | |--------|------|---------|-------------| | storageType | 'localStorage' \| 'sessionStorage' | 'localStorage' | Which storage backend to watch |

// Watch localStorage (default)
const watcher = new StorageWatcher();

// Watch sessionStorage
const sessionWatcher = new StorageWatcher({ storageType: 'sessionStorage' });

watcher.on(key, callback)Unsubscribe

Watch a specific key for changes. Works even if the key doesn't exist yet — the callback fires the moment the key is created.

// Watch an existing key
watcher.on('theme', (newValue, oldValue) => {
  console.log(`Theme changed from ${oldValue} to ${newValue}`);
});

// Watch a key that doesn't exist yet (future key)
watcher.on('future-config', (newValue, oldValue) => {
  // oldValue will be null (key didn't exist before)
  console.log(`Config was created with value: ${newValue}`);
});

Callback signature: (newValue: string | null, oldValue: string | null) => void

  • newValue is null when the key is removed or cleared
  • oldValue is null when the key is newly created

watcher.onAny(callback)Unsubscribe

Watch all key changes across the storage.

watcher.onAny((key, newValue, oldValue) => {
  console.log(`${key} changed: ${oldValue} → ${newValue}`);
});

Callback signature: (key: string, newValue: string | null, oldValue: string | null) => void


watcher.onNew(callback)Unsubscribe

Watch for brand-new keys being added to storage. Fires only when a key that didn't previously exist is created via setItem().

watcher.onNew((key, value) => {
  console.log(`New key "${key}" created with value: ${value}`);
});

localStorage.setItem('brand-new', 'hello');  // ✅ fires (new key)
localStorage.setItem('brand-new', 'world');  // ❌ doesn't fire (key already exists)
localStorage.removeItem('brand-new');
localStorage.setItem('brand-new', 'again');  // ✅ fires (key was removed, now re-created)

Callback signature: (key: string, value: string) => void


watcher.destroy()

Destroy the watcher instance. Removes all listeners and cleans up internal patches. After calling destroy(), any calls to on(), onAny(), or onNew() will throw an AlreadyDestroyedError.

watcher.destroy();

This method is idempotent — calling it multiple times is safe.


Unsubscribe

All watch methods return an unsubscribe function:

const unsub = watcher.on('key', callback);

// Stop watching this specific callback
unsub();

// Calling again is safe (idempotent)
unsub();

How It Works

The package monkey-patches Storage.prototype.setItem, .removeItem, and .clear to intercept mutations. Key details:

  • Reference counted — patches are installed when the first StorageWatcher instance is created and restored when the last instance is destroyed
  • Multiple instances — you can create multiple watchers (even for different storage types) and they coexist safely
  • clear() handling — when clear() is called, individual events fire for each key that existed, so per-key watchers work correctly
  • Error isolation — if one callback throws, other callbacks and the storage operation itself are not affected

Events Fired

| Storage Operation | on(key) | onAny() | onNew() | |-------------------|-----------|-----------|-----------| | setItem(key, value) — new key | ✅ | ✅ | ✅ | | setItem(key, value) — existing key | ✅ | ✅ | ❌ | | removeItem(key) | ✅ | ✅ | ❌ | | clear() | ✅ (per key) | ✅ (per key) | ❌ |

Error Handling

| Error | When | |-------|------| | StorageUnavailableError | Storage backend is not available (e.g., private browsing) | | AlreadyDestroyedError | Method called on a destroyed instance |

Both extend StorageWatcherError, which extends Error.

import { StorageWatcherError, AlreadyDestroyedError } from 'storage-watcher';

try {
  watcher.on('key', cb);
} catch (err) {
  if (err instanceof AlreadyDestroyedError) {
    // handle destroyed instance
  }
}

TypeScript

Full TypeScript support with exported types:

import type {
  StorageWatcherOptions,
  StorageType,
  WatchCallback,
  WatchAnyCallback,
  WatchNewCallback,
  Unsubscribe,
} from 'storage-watcher';

License

MIT