Fallback Chains
Fallback chains allow you to use multiple storage adapters together. This is useful for migration scenarios, redundancy, or progressive enhancement.
How It Works
When you pass an array of adapters to createStorageStore:
- Read: Returns the first non-null value found (in order)
- Write: Writes to ALL adapters
- Listen: Listens to the FIRST adapter only
- Remove: Removes from ALL adapters
import { createStorageStore, sessionStorageAdapter, localStorageAdapter,} from "@vp-tw/nanostores-storage";
const store = createStorageStore( [sessionStorageAdapter, localStorageAdapter], // Fallback chain "user-data",);Use Cases
Session with Persistence
Store data in sessionStorage (fast, tab-isolated) with localStorage backup:
const store = createStorageStore([sessionStorageAdapter, localStorageAdapter], "draft-content");
// Read: Prefers sessionStorage, falls back to localStorage// Write: Saves to both (redundancy)Migration Strategy
When migrating from one storage to another:
// Old: data was in localStorage// New: prefer sessionStorage
const store = createStorageStore([sessionStorageAdapter, localStorageAdapter], "legacy-key");
// Reads old data from localStorage if sessionStorage is empty// New writes go to both, gradually migrating dataCross-Subdomain with Local Fallback
Combine cookies (cross-subdomain) with localStorage (faster):
import { createStorageStore, localStorageAdapter } from "@vp-tw/nanostores-storage";import { createCookieAdapter } from "./my-cookie-adapter";
const cookieAdapter = createCookieAdapter();
const store = createStorageStore([localStorageAdapter, cookieAdapter], "shared-session");Read Order Behavior
// Scenario: sessionStorage is empty, localStorage has "cached-value"
const store = createStorageStore([sessionStorageAdapter, localStorageAdapter], "my-key");
store.get(); // Returns "cached-value" (from localStorage)
// After setting a value:store.set("new-value");
// Both now have "new-value"sessionStorage.getItem("my-key"); // "new-value"localStorage.getItem("my-key"); // "new-value"Listening Behavior
With listen: true, the store only reacts to changes from the first adapter in the chain:
const store = createStorageStore([sessionStorageAdapter, localStorageAdapter], "my-key", { listen: true,});
// Only sessionStorage changes trigger store updates// localStorage changes do NOT trigger updatesThis design is intentional for the primary use case: maintaining tab-isolated state with a persistent fallback.
Example: [sessionStorage, localStorage]
- Same tab: Only listens to sessionStorage (per-tab isolation)
- New tab: sessionStorage is empty → reads from localStorage
- Write: Saves to both adapters
This ensures each tab maintains its own state while new tabs start with the last persisted value.
Important Notes
- Write Order: Values are written to adapters in array order
- Atomic Operations: There’s no transaction — if one adapter fails, others may succeed
- Performance: More adapters = more I/O operations
- Storage Limits: Each adapter has its own limits
Example: Complete Setup
import { createStorageStore, sessionStorageAdapter, localStorageAdapter,} from "@vp-tw/nanostores-storage";
// User preferences with session overrideconst prefsStore = createStorageStore( [sessionStorageAdapter, localStorageAdapter], "user-preferences", { defaultValue: JSON.stringify({ theme: "system", language: "en" }), listen: true, },);
// Helper to work with JSONfunction getPrefs() { const raw = prefsStore.get(); return raw ? JSON.parse(raw) : { theme: "system", language: "en" };}
function setPrefs(prefs: { theme: string; language: string }) { prefsStore.set(JSON.stringify(prefs));}Next Steps
- Cross-Tab Sync — How listening works
- Custom Adapters — Create your own adapters