npm install rune-sync
import { lsSync } from 'rune-sync/localstorage';
let settings = lsSync('settings', { theme: 'dark', lang: 'en' });
settings.theme = 'light'; // Persisted automatically
Note: State must always be an object — primitives are not supported as root values.
import { lsSync } from 'rune-sync/localstorage';
let state = lsSync('key', { count: 0 });
Cross-tab sync via the native storage event. No extra dependencies.
npm install localforage
import { lfSync } from 'rune-sync/localforage';
let state = lfSync('key', { count: 0 });
Cross-tab sync via BroadcastChannel. Requires localforage as a peer dependency.
import { ckSync } from 'rune-sync/cookie';
// Default options (path: '/', sameSite: 'lax')
let state = ckSync('key', { count: 0 });
With custom cookie settings:
import { createCookieSync } from 'rune-sync/cookie';
const cookieSync = createCookieSync({
path: '/',
maxAge: 86400,
sameSite: 'strict',
secure: true
});
let state = cookieSync('key', { count: 0 });
Cross-tab sync via BroadcastChannel. Keep in mind the ~4KB cookie size limit.
CookieOptions:
| Option | Type | Default |
|---|---|---|
path |
string |
'/' |
domain |
string |
— |
maxAge |
number (seconds) |
— |
expires |
Date |
— |
sameSite |
'strict' | 'lax' | 'none' |
'lax' |
secure |
boolean |
— |
All drivers accept an optional third argument:
let state = lsSync('key', { value: 0 }, {
debounce: 500, // Wait 500ms after last change before writing
throttle: 1000, // Write at most once per 1000ms
doNotSubscribe: true // Disable cross-tab sync
});
| Option | Type | Description |
|---|---|---|
debounce |
number |
Delay writes by N ms after the last change |
throttle |
number |
Write at most once per N ms (with trailing) |
doNotSubscribe |
boolean |
Disable cross-tab synchronization |
Implement the StateSynchronizer interface to use any storage backend:
import { createSyncState } from 'rune-sync';
import type { StateSynchronizer } from 'rune-sync';
const mySynchronizer: StateSynchronizer = {
read: async (key) => {
const data = await myStorage.get(key);
return data ?? null;
},
write: async (key, value) => {
await myStorage.set(key, value);
},
// Optional: enable real-time updates
subscribe: (key, write) => {
const unsubscribe = myRealtimeService.on(key, (data) => {
write(data);
});
return unsubscribe;
}
};
const mySync = createSyncState(mySynchronizer);
let state = mySync('app-state', { counter: 0 });
StateSynchronizer Interfaceinterface StateSynchronizer {
read<T>(key: string): Promise<T | null> | T | null;
write<T>(key: string, value: T): Promise<void> | void;
subscribe?<T>(key: string, write: (newValue: T) => void): () => void;
}
MIT License - see LICENSE for details.