A lightweight wrapper around localStorage and sessionStorage that adds TTL expiration, LZString compression, namespacing, batch operations, and cross-tab sync — all async, all Promise-based.
npm / yarn
npm install web-storage-manager-js
ES module
import StorageManager from 'web-storage-manager-js';
CDN
<script src="https://cdn.jsdelivr.net/npm/web-storage-manager-js@1.0.1/StorageManager.js"></script>
new StorageManager(useSession?, options?)
| Option | Type | Default | Description |
|---|---|---|---|
useSession |
boolean | false | Use sessionStorage instead of
localStorage. |
namespace |
string | "" | Prefix prepended to every key to isolate modules sharing an origin. |
defaultExpiration |
object | {} | Map of { keyName: seconds }.
Auto-expires matching keys on set(). |
enableCompression |
boolean | true | Compress values via LZString before writing (lazy-loaded from CDN on first use). |
// localStorage, no namespace, compression on (default)
const store = new StorageManager();
// sessionStorage
const session = new StorageManager(true);
// Namespaced with auto-expiration
const appStore = new StorageManager(false, {
namespace: 'myApp',
defaultExpiration: { authToken: 3600, userPrefs: 86400 },
enableCompression: false,
});
All methods are async and return
Promises.
| Method | Description |
|---|---|
set(key, value) |
Store a JSON-serializable value. Auto-expires if
defaultExpiration includes the key. |
get(key) |
Retrieve a value, or null if missing or expired.
Expired items are removed on read. |
remove(key) |
Delete a key and cancel any pending expiration timer. |
has(key) |
Returns true if the key exists and has not
expired. |
expires(key, seconds) |
Set or update the TTL for an existing key. |
keys() |
Array of all keys in the active namespace (without prefix). |
getAll() |
All key-value pairs under the active namespace as a plain object. |
batchSet(items) |
Write multiple
{ key, value, expiresIn? } pairs at once. |
batchGet(keys) |
Read multiple keys at once. Returns an object mapping each
key to its value or null. |
batchRemove(keys) |
Remove an array of keys in one call. |
onChange(key, cb) |
Register a listener called with
(newValue, oldValue) on any change, including cross-tab. |
offChange(key) |
Unsubscribe the listener registered for key.
|
cleanup() |
Scan and remove all expired keys. Called automatically on construction. |
clear() |
Remove all entries from the underlying storage object. |
Caching an API response
const cache = new StorageManager(false, {
namespace: 'apiCache',
defaultExpiration: { products: 300 }, // 5 min
});
async function getProducts() {
if (await cache.has('products')) return cache.get('products');
const data = await fetch('/api/products').then(r => r.json());
await cache.set('products', data); // auto-expires
return data;
}
Auth token with TTL
const auth = new StorageManager(false, {
namespace: 'auth',
enableCompression: false,
});
await auth.set('token', tokenValue);
await auth.expires('token', expiresInSeconds);
// Returns null if expired
const token = await auth.get('token');
Cross-tab sync
// Tab A — listen for changes
store.onChange('notifications', (count) => {
updateBadge(count);
});
// Tab B — triggers Tab A's listener
await store.set('notifications', 5);
TTL Expiration
Per-key time-to-live with auto-removal and reactive
onChange notification on expiry.
LZString Compression
Transparent compression keeps large payloads well under the 5 MB storage limit.
Namespacing
Prefix all keys to isolate modules sharing the same origin — no collisions.
Batch Operations
batchSet, batchGet, and batchRemove for multi-key ops in one call.
Enable when
Disable when
Data written with compression on cannot be read back with it off — pick one mode per namespace.