A lightweight, generic state management class for Svelte 5 projects that bridges in-memory reactive state with persistent storage. Work with Svelte 5 $state
runes, letting you sync with your chosen storage backend (localStorage, APIs, databases, etc.).
$state
rune for automatic reactivityCopy the StateManager.ts
file into your Svelte 5 project.
import { StateManager } from './StateManager';
interface AppSettings {
theme: 'light' | 'dark';
language: string;
notifications: boolean;
}
// Create a state manager that syncs in-memory state with localStorage
const settingsManager = new StateManager<AppSettings>(
// Load function - retrieve from persistent storage → memory
async () => {
const saved = localStorage.getItem('app-settings');
return saved
? JSON.parse(saved)
: {
theme: 'light',
language: 'en',
notifications: true,
};
},
// Save function - persist memory changes → storage
async data => {
localStorage.setItem('app-settings', JSON.stringify(data));
},
// Debounce options - optimize storage writes
{ delay: 500, maxWait: 2000 }
);
// Initialize: Load from persistent storage into reactive memory
await settingsManager.load();
// Export state
export let settings = settingsManager.state;
<script lang="ts">
import { settingsManager } from './stores/settings';
// The state is reactive in-memory - changes instantly update the UI
let { state } = settingsManager;
function toggleTheme() {
state.theme = state.theme === 'light' ? 'dark' : 'light';
}
</script>
<button on:click={toggleTheme}>
Current theme: {state.theme} <!-- Updates instantly -->
</button>
<label>
<input
type="checkbox"
bind:checked={state.notifications}
/>
Enable notifications
</label>
How it works:
new StateManager<T>(
loadCallback: () => Promise<T> | T,
saveCallback?: (state: T) => Promise<void> | void,
debounceOptions?: DebounceOptions
)
loadCallback
: Function to load initial state datasaveCallback
(optional): Function to persist state changesdebounceOptions
(optional): Configuration for save debouncingdelay
: Milliseconds to wait after last change (default: 0)maxWait
: Maximum milliseconds before forcing save (default: 0)immediate
: Execute save immediately on first change (default: false)state
: The reactive state object (Svelte 5 $state
)load()
: Loads state from persistent storage into reactive memorysave()
: Manually triggers a sync from memory to persistent storageT
must be structured-cloneable/serializablemanager.state.theme = 'dark'
$state.snapshot(manager.state)
to get a non-reactive copy for external useload()
calls in try-catch blocksconst manager = new StateManager(loadFn, saveFn, {
delay: 300, // Wait 300ms after last change
maxWait: 2000, // Force save after 2 seconds maximum
immediate: true, // Save immediately on first change
});
// Separate managers for different concerns
const userSettings = new StateManager(loadUserSettings, saveUserSettings);
const appCache = new StateManager(loadCache, saveCache, { delay: 100 });
const gameState = new StateManager(loadGame, saveGame, { immediate: true });