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. You cannot use primitive values (like strings, numbers, or booleans) as the root state. Always wrap your data in an object.
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