Cross-Tab Sync
nanostores-storage can listen for storage changes from other browser tabs or windows. This enables real-time synchronization across your application.
Enabling Cross-Tab Sync
Option 1: Enable at Creation
import { createStorageStore, localStorageAdapter } from "@vp-tw/nanostores-storage";
const store = createStorageStore(localStorageAdapter, "shared-key", { listen: true, // Start listening immediately});Option 2: Control Manually
const store = createStorageStore(localStorageAdapter, "shared-key");
// Start listeningstore.listener.on();
// Check statusconsole.log(store.listener.$on.get()); // true
// Stop listeningstore.listener.off();How It Works
When listen is enabled:
- The adapter subscribes to
StorageEventon thewindowobject - When storage changes externally, the event fires
- The store re-reads from storage and updates its value
- Your subscribers are notified of the change
// Tab 1const store = createStorageStore(localStorageAdapter, "counter", { listen: true,});
store.$value.subscribe((value) => { console.log("Counter:", value); // Logs when Tab 2 changes it});
// Tab 2localStorage.setItem("counter", "42");// Tab 1 automatically updates!Listener State
The listener state is a reactive atom:
import { useStore } from "@nanostores/react";
function SyncIndicator() { const isListening = useStore(store.listener.$on);
return ( <span> {isListening ? "Syncing enabled" : "Syncing disabled"} </span> );}localStorage vs sessionStorage
| Feature | localStorage | sessionStorage |
|---|---|---|
| Cross-tab events | ✅ Yes | ❌ No* |
| Persistence | Until cleared | Until tab closes |
*sessionStorage is isolated per tab, so there are no cross-tab events to listen for.
Performance Considerations
- Each listener adds a
storageevent handler towindow - Event handlers are reference-counted and cleaned up automatically
- Multiple stores can share the same underlying event listener
// These two stores share the same event listener internallyconst store1 = createStorageStore(localStorageAdapter, "key1", { listen: true });const store2 = createStorageStore(localStorageAdapter, "key2", { listen: true });Example: Real-Time Theme Sync
import { createStorageStore, localStorageAdapter } from "@vp-tw/nanostores-storage";
export const themeStore = createStorageStore(localStorageAdapter, "theme", { defaultValue: "system", listen: true,});
// Apply theme on changethemeStore.$value.subscribe((theme) => { document.documentElement.dataset.theme = theme ?? "system";});Now when you change the theme in one tab, all other tabs update instantly.
Cleanup
When using manual control, remember to stop listening when appropriate:
// In a componentuseEffect(() => { store.listener.on();
return () => { store.listener.off(); };}, []);With listen: true at creation, the listener runs for the lifetime of the store.
Next Steps
- createStorageStore API — Full API reference
- Custom Adapters — Implement custom storage backends