rune-sync Svelte Themes

Rune Sync

Svelte 5 library for synchronizing reactive state across various storage backends

rune-sync

Svelte 5 reactive state that automatically persists to any storage backend.

Quick Start

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.

Drivers

localStorage

import { lsSync } from 'rune-sync/localstorage';

let state = lsSync('key', { count: 0 });

Cross-tab sync via the native storage event. No extra dependencies.

localForage (IndexedDB)

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

Settings

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

Custom Synchronizers

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 Interface

interface 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;
}

License

MIT License - see LICENSE for details.

Top categories

Loading Svelte Themes