Package overview
Namespace it. Expire it. Upgrade it — without localStorage glue.
Persist app data in the browser — with namespaces, expiry, and schema upgrades — without reinventing localStorage helpers.
Think of Storage as a small policy layer on top of an adapter you choose (memory, localStorage, sessionStorage, or IndexedDB). You call set / get; Storage wraps values in an envelope (metadata + your data).
Start here
New to the package? → Tutorial (10 minutes)
Know localStorage already? → skim Core concepts, then Recipes
Need the full API? → Core · TypeDoc
In one minute
ts
import { createStorage, createLocalStorageAdapter } from "@jayoncode/storage";
const storage = createStorage({
namespace: "app", // all keys live under "app:…"
adapter: createLocalStorageAdapter(),
});
storage.set("theme", "dark");
storage.get("theme"); // "dark" | nullThat is enough for many apps. Everything else (TTL, policies, migrate, cleanup, …) is optional.
Pick your path
| You are… | Read this | Then |
|---|---|---|
| Beginner — first time using Storage | Tutorial | Concepts → Recipes |
| Shipping an app — prefs, cache, SSR | Core · Errors | Best practices · Browser support |
| Advanced — GC, backup, events, IDB, cross-tab, quota | Maintenance → Quota · Transforms | Composition |
When to use
- Theme / UI prefs that should survive reload
- Short-lived cache next to longer-lived prefs
- Explicit backends: memory in tests,
localStoragein the browser - Optional: cleanup expired keys, export/import, in-process watchers, same-tab transactions
When not to use
| Need | Use instead |
|---|---|
| Live UI state (React/Vue store) | Your framework state |
| Form draft UX | Form Intelligence |
| Queryable / relational IndexedDB | @jayoncode/storage/async is key/value IDB — not a query engine |
| Passwords / tokens | Don’t put secrets in web storage (Security) |
Install
bash
npm install @jayoncode/storagebash
pnpm add @jayoncode/storagebash
yarn add @jayoncode/storageExample: prefs vs cache (policies)
Named policies are just reusable TTL presets — beginners can skip them and pass { ttl: … } on each set.
ts
import { createStorage, createLocalStorageAdapter } from "@jayoncode/storage";
const storage = createStorage({
namespace: "app",
adapter: createLocalStorageAdapter(),
policies: {
preferences: { ttl: { days: 365 } },
cache: { ttl: { minutes: 15 } },
},
});
storage.set("theme", "dark", { policy: "preferences" });
storage.set("feed", data, { policy: "cache" });How the pieces fit
mermaid
flowchart LR
App["Your app"] --> CS["createStorage"]
CS --> Adapter["Adapter\n(memory / local / session)"]
CS --> Env["Envelope\nvalue + savedAt + expiresAt?"]
Adapter --> Browser["Browser storage"]| Piece | Plain English |
|---|---|
| Namespace | Folder name for keys (app:theme) |
| Adapter | Where bytes live |
| Envelope | Your value + metadata Storage stores for you |
| Policy | Named default TTL for a kind of write |
| Subpath | Extra tools you import only when needed |
Problem → approach
| Pain today | What Storage does |
|---|---|
Hand-rolled JSON.parse + key prefixes | One createStorage per concern |
| Expiry logic copied into every reader | TTL on write; auto-drop when you read |
| Old tabs break after a schema change | schemaVersion + migrate on get |
| Core API gets huge with GC / backup / watch | Those live on subpaths |
Cheatsheet
| I want to… | Call |
|---|---|
| Save | set(key, value) |
| Load | get(key) → value or null |
| See expiry / version | peek(key) |
| Check without migrating | has(key) |
| Delete one key | remove(key) |
| Wipe this namespace | clear() |
| Sweep expired (advanced) | cleanup from /maintenance |
Documentation map
Beginner
| Guide | What you’ll learn |
|---|---|
| Tutorial | Install → set/get/peek → first policy |
| Core concepts | Envelope, TTL, adapters in plain language |
| Recipes | Copy-paste prefs, cache, lists |
Intermediate
| Guide | What you’ll learn |
|---|---|
| Core | All options, migrate, adapters |
| Errors | Throws vs soft null |
| Best practices | Naming, SSR, what not to store |
| FAQ · Browser support · Security | Edge cases |
Advanced
| Guide | What you’ll learn |
|---|---|
| Maintenance | Explicit GC report |
| Snapshots | Export / restore |
| Observable | In-process watch / on |
| Diagnostics | DEV report / activity |
| Transactions | Same-tab rollback |
| Async / IndexedDB | Promise API + IndexedDB adapter |
| Cross-tab | Notify other tabs (no auto-merge) |
| Quota | Soft limits (approx bytes) |
| Transforms | Opt-in compress / encrypt hooks |
| Composition | Wire with BL / FI / OD in app code |
Reference
- API (TypeDoc) — every export
- Playground guide
- Open Lab
- Draft Desk — official reference app (draft envelopes + TTL in a live form flow)
