A powerful and flexible Svelte 5 library for synchronizing reactive state across various storage backends, enabling seamless data persistence and real-time collaboration.
npm install rune-sync
# or
pnpm add rune-sync
# or
yarn add rune-sync
import { createSyncState } from 'rune-sync';
import { lsSync } from 'rune-sync/localstorage';
// Create a reactive state that persists to localStorage
let userSettings = lsSync('user-settings', {
theme: 'dark',
language: 'en'
});
// State changes are automatically saved
userSettings.theme = 'light'; // Persisted immediately
Yes, that's all it takes to get started!
Limitation: Rune-Sync state must always be an object or array. You cannot use primitive values (like strings, numbers, or booleans) as the root state. Always wrap your data in an object or array.
import { createSyncState } from 'rune-sync';
import { lsSync } from 'rune-sync/localstorage';
// Create a reactive state that persists to localStorage
let userSettings = lsSync('user-settings', {
theme: 'dark',
language: 'en'
});
// State changes are automatically saved
userSettings.theme = 'light'; // Persisted immediately
import { lfSync } from 'rune-sync/localforage';
// Debounced writes (wait 500ms after changes before saving)
let searchResults = lfSync(
'search-results',
{
query: '',
results: []
},
{ debounce: 500 }
);
// Throttled writes (save at most once per 1000ms)
let realTimeData = lfSync(
'realtime-data',
{
value: 0
},
{ throttle: 1000 }
);
// Disable cross-tab synchronization
let localOnlyState = lsSync(
'local-only',
{
data: 'sensitive'
},
{ doNotSubscribe: true }
);
localStorageSync: Browser localStorage with cross-tab synchronization via Storage APIlocalForageSync: IndexedDB/localStorage via localForage with cross-tab synchronization via BroadcastChannelNote: If you want to use the
localForagesynchronizer, you must also installlocalforage
Cross-tab Sync: Both built-in synchronizers automatically synchronize state changes across browser tabs/windows
Implement the StateSynchronizer interface to create your own storage backend:
import type { StateSynchronizer } from 'rune-sync';
const myCustomSync: StateSynchronizer = {
read: async (key: string) => {
// Implement your read logic
const data = await myStorage.get(key);
return data ? JSON.parse(data) : null;
},
write: async (key: string, value: unknown) => {
// Implement your write logic
await myStorage.set(key, JSON.stringify(value));
},
// Optional: Enable real-time updates
subscribe: (key: string, write: (newValue: T) => void) => {
// Set up real-time listener
// Call write() with the new value when it changes (when event occurs)
const unsubscribe = myRealtimeService.subscribe(key, (data) => {
write(data.value);
});
return unsubscribe;
}
};
// Use your custom synchronizer
const syncState = createSyncState(myCustomSync);
let appState = syncState('app-state', { counter: 0 });
createSyncState(synchronizer: StateSynchronizer)Creates a state factory function that uses the provided synchronizer.
Parameters:
synchronizer: Implementation of the StateSynchronizer interfaceReturns: A function that creates synchronized reactive state
const syncState = createSyncState(synchronizer);
const state = syncState<T>(key: string, initialValue: T, settings?: SyncSettings): T;
Parameters:
key: Storage key for the stateinitialValue: Initial state value (must be object or array)settings: Optional configuration objectReturns: Reactive Svelte state
SyncSettings Interfaceinterface SyncSettings {
// Disable cross-tab synchronization
doNotSubscribe?: boolean;
// Debounce writes by N milliseconds
debounce?: number;
// Throttle writes to at most once per N milliseconds
throttle?: number;
}
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;
}
We welcome contributions! Please see our contributing guidelines for details.
MIT License - see LICENSE for details.
Made for the Svelte community