Comprehensive Web Storage ponyfills and polyfiils for `localStorage`, `sessionStorage`, `StorageEvent`, and `Storage` interfaces. Includes manual and auto-shim support.
Overview
This package provides in-memory ponyfills for the Web Storage APIs Storage,
localStorage, sessionStorage, and StorageEvent. It also provides optional
polyfill support through the ./install and ./shim entry points.
Features
- Complete Storage API: Full
Storageinterface with all standard methods defined by the Web Storage API specification. - localStorage + sessionStorage: In-memory ponyfills ensure both APIs are always available regardless of the runtime environment you're in.
- Cross-platform compatibility: Works seamlessly in Deno, Bun, Node, Cloudflare Workers, browsers, and any other ES2015+ capable environment.
- TypeScript support: Written in 100% TypeScript. Includes declarations for all public APIs, ensuring accurate type checking, intellisense, and autocompletion regardless of your IDE preference.
- Zero dependencies: Entirely self-contained and dependency-free!
- Standards compliant: Follows the Web Storage API specification to a T.
- 🦄 Ponyfills! While this package does include polyfills on an opt-in
basis, it primarily provides (and strongly recommends) 100% non-invasive
ponyfills that don't modify the global scope unless explicitly requested.
This ensures maximum compatibility, and guarantees the global scope will only be modified when you specifically ask for it. No surprises here!
Install
deno add jsr:@nick/storage
yarn add jsr:@nick/storage
pnpm add jsr:@nick/storage
bunx jsr add @nick/storage
npx jsr add @nick/storage
Usage
The recommended usage for this package is to import the ponyfill directly in your code and use it as needed. All APIs are exposed as named exports from the root entry point of the package.
Ponyfill
// import the ponyfill implementations as needed import { localStorage, sessionStorage, Storage, type StorageEvent, } from "@nick/storage"; // Use localStorage for persistent storage localStorage.setItem("user", "john"); const user = localStorage.getItem("user"); // 'john' // Use sessionStorage for session-based storage sessionStorage.setItem("temp", "data"); const temp = sessionStorage.getItem("temp"); // 'data' // Use the Storage constructor for custom storage instances const customStorage = new Storage(); // Listen for storage events (in environments that support it) if (typeof globalThis !== "undefined" && globalThis.addEventListener) { globalThis.addEventListener("storage", (event: StorageEvent) => { console.log(`Storage key changed: ${event.key}`); console.log(`Old value: ${event.oldValue}`); console.log(`New value: ${event.newValue}`); console.log(`URL of page that made the change: ${event.url}`); console.log( `Storage area: ${ event.storageArea === localStorage ? "localStorage" : "sessionStorage" }`, ); }); }
What is a Ponyfill?
A ponyfill is a polyfill that doesn't modify global objects. Instead, it provides the functionality as importable modules, giving you full control over how and when to use them.
Unlike polyfills that automatically modify global objects, ponyfills:
- Don't pollute the global scope with opinionated implementations
- Enable selective usage of features without affecting other code
- Provide better testability, isolation, and modularity
- Integrate well with future native implementations: you decide when to switch.
- Avoid conflicts with external libraries
Polyfill (graceful global installation)
If you need the Storage APIs to be available globally (like in a browser), you
can install them using the install functions exported from the ./install
entry point. These functions will only install the APIs if they are not
already available in the global scope.
import { install } from "@nick/storage/install"; const result = install(); if (result.type === "success") { console.log("Storage APIs installed successfully"); // Now you can use localStorage and sessionStorage globally localStorage.setItem("key", "value"); sessionStorage.setItem("temp", "data"); } else if (result.type === "skipped") { console.log("Storage APIs already available"); } else { console.error("Failed to install Storage APIs:", result.error); }
Shim (auto-install)
For convenience, you can auto-install the global APIs by importing the shim
entry point of this package via a side-effect import. This is a convenience
module that provides no exports — importing it is equivalent to calling the
install function from the ./install entry point.
import "@nick/storage/shim"; // Storage APIs are now available globally localStorage.setItem("key", "value"); sessionStorage.setItem("temp", "data");
The shim entry point automatically installs the Storage APIs globally if
they are not already present. Use with care — modifying the global scope
could lead to conflicts with other libraries/future native implementations.
CDN Usage (via esm.sh)
JavaScript/TypeScript Modules (HTTPS imports)
import { localStorage, sessionStorage, Storage, type StorageEvent, } from "https://esm.sh/jsr/@nick/storage"; // Use the Storage APIs as needed
import "https://esm.sh/jsr/@nick/storage/shim"; // Storage APIs are now available globally
HTML
<script type="module"> import { localStorage, sessionStorage, Storage, type StorageEvent, } from "https://esm.sh/jsr/@nick/storage"; // Use the Storage APIs as needed </script>
<script src="https://esm.sh/jsr/@nick/storage/shim"></script> <!-- Storage APIs are now available globally -->
API
Storage API
The Storage class implements the complete Web Storage API interface.
class Storage { readonly length: number; clear(): void; getItem(key: string): string | null; key(index: number): string | null; removeItem(key: string): void; setItem(key: string, value: string): void; [index: number]: string; }
All storage instances support these methods:
clear(): void
Removes all key-value pairs from storage.
import { Storage } from "@nick/storage"; const storage = new Storage(); storage.setItem("foo", "bar"); storage.clear(); // all items removed
getItem(key: string): string | null
Returns the value associated with the key, or null if the key doesn't exist.
import { localStorage } from "@nick/storage"; const value = localStorage.getItem("username"); console.log(`Username: ${value}`);
key(index: number): string | null
Returns the key at the specified index, or null if the index is out of range.
import { sessionStorage } from "@nick/storage"; const value = sessionStorage.getItem("token"); console.log(`Token: ${value}`);
removeItem(key: string): void
Removes the key-value pair from storage.
import { localStorage } from "@nick/storage"; localStorage.removeItem("theme");
setItem(key: string, value: string): void
Sets the value for the given key. The value is always stored as a string.
import { localStorage } from "@nick/storage"; localStorage.setItem("theme", "dark");
localStorage
Persistent storage that maintains data across sessions. In browser environments, this data persists even after the browser is closed and reopened.
This ponyfill has no built-in persistence mechanism, and therefore both its
localStorage and sessionStorage implementations are in-memory only.
They do not persist data across application restarts.
import { localStorage } from "@nick/storage"; import assert from "node:assert"; // store data localStorage.setItem("username", "alice"); localStorage.setItem("preferences", JSON.stringify({ theme: "dark" })); // retrieve data const username = localStorage.getItem("username"); // 'alice' const prefs = JSON.parse(localStorage.getItem("preferences") || "{}"); assert.strictEqual(username, "alice"); assert.deepStrictEqual(prefs, { theme: "dark" }); // remove specific item localStorage.removeItem("username"); // clear all data localStorage.clear(); assert.strictEqual(localStorage.length, 0);
import { localStorage } from "@nick/storage"; import assert from "node:assert"; localStorage.setItem("session_id", "xyz789"); localStorage.setItem("auth_token", "token_123"); // check storage length console.log( `${localStorage.length} items in local storage`, // 2 ); // get key by index const firstKey = localStorage.key(0); // 'session_id' assert.strictEqual(firstKey, "session_id"); // indexed property access per specification const sessionId = localStorage["session_id"] ||= "xyz789"; // 'xyz789' assert.strictEqual(sessionId, localStorage["session_id"]); // 'xyz789'
If you absolutely need persistence, consider using the native localStorage
API which is supported by the latest versions of Node.js, Bun, and Deno.
sessionStorage
Session-based storage that clears when the session ends. In browser environments, this data is cleared when the tab is closed.
import { sessionStorage } from "@nick/storage"; import assert from "node:assert"; // store temporary data sessionStorage.setItem("csrf_token", "abc123"); sessionStorage.setItem("form_data", JSON.stringify({ step: 2 })); // retrieve data const token = sessionStorage.getItem("csrf_token"); // 'abc123' const formData = JSON.parse(sessionStorage.getItem("form_data") || "{}"); assert.strictEqual(token, "abc123"); assert.deepStrictEqual(formData, { step: 2 }); // check storage length console.log(`${sessionStorage.length} items in session storage`); // get key by index const firstKey = sessionStorage.key(0); assert.strictEqual(firstKey, "csrf_token"); // clear session data sessionStorage.clear(); assert.strictEqual(sessionStorage.length, 0);
StorageEvent
This package also includes a StorageEvent implementation to match the Web
Storage specification. StorageEvents are fired when changes are made to Storage
instances like localStorage or sessionStorage in another context (e.g. a
different browser tab/window/frame that shares the same origin).
Basic Usage
In browser environments that support it, StorageEvent is fired automatically
when storage changes in a different window or tab, or from other contexts like
iframes or service workers.
Creating StorageEvent Manually
import { StorageEvent } from "@nick/storage"; const event = new StorageEvent("storage", { key: "theme", oldValue: "light", newValue: "dark", url: "https://example.com", storageArea: localStorage, }); // If in a browser environment, you could dispatch it if ( typeof globalThis.navigator !== "undefined" && !globalThis.navigator.userAgent?.includes("Deno") && typeof globalThis.dispatchEvent === "function" ) globalThis.dispatchEvent(event);
Listening for Storage Events
import "@nick/storage/shim"; // Add a listener for storage events addEventListener("storage", (event) => { console.log(`Storage key changed: ${event.key}`); console.log(`Old value: ${event.oldValue}`); console.log(`New value: ${event.newValue}`); console.log(`URL of page that made the change: ${event.url}`); console.log( `Storage area: ${ event.storageArea === localStorage ? "localStorage" : "sessionStorage" }`, ); });
In Deno or other non-browser environments, storage events are simulated when modifications are made to the storage objects. These events are only dispatched in the same context where the change was made.
Creating StorageEvent Manually
import { StorageEvent } from "@nick/storage"; const event = new StorageEvent("storage", { key: "user-preferences", oldValue: '{"theme":"light"}', newValue: '{"theme":"dark"}', url: "https://example.com", storageArea: localStorage, }); // If in a browser environment, you could dispatch it if ( typeof globalThis.navigator !== "undefined" && !globalThis.navigator.userAgent?.includes("Deno") && typeof globalThis.dispatchEvent === "function" ) { globalThis.dispatchEvent(event); }
StorageEvent Properties
key: The key being changed (string)oldValue: The previous value, or null for new itemsnewValue: The new value, or null if the key was removedurl: The URL of the page where the change occurredstorageArea: The storage object that was modified (localStorage or sessionStorage)
StorageEvent Methods
initStorageEvent(): Legacy method to initialize the event (similar toEvent.initEvent)
Environment Compatibility
This package is fully supported in the following environments:
- Deno
- Node.js
- Bun
- Cloudflare Workers
- Deno Deploy
- Netlify Functions
- Browsers (though native implementations are preferred when available)
- any other ES2015+ capable environment
Contributing
Contributions are always welcome! Please open an issue to discuss any proposed changes before submitting a Pull Request.
Add Package
deno add jsr:@nick/storage
Import symbol
import * as storage from "@nick/storage";
Import directly with a jsr specifier
import * as storage from "jsr:@nick/storage";