A small, robust utility for reactive, persisted state in Svelte, with first-class support for debouncing, tab synchronization, and custom storage behavior.
Designed to feel like normal Svelte state — just persistent.
localStorage, sessionStorage, or custom Storagenpm install svelte-persisted-state
or
pnpm add svelte-persisted-state
import { persisted } from 'svelte-persisted-state';
const counter = persisted('counter', 0);
counter.value += 1;
localStorage<script lang="ts">
import { persisted } from 'svelte-persisted-state';
const theme = persisted('theme', 'light');
</script>
<button on:click={() => theme.value = 'dark'}>
Dark mode
</button>
<p>Current theme: {theme.value}</p>
persisted<T>(key, fallback, behavior?)Creates (or retrieves) a persisted, reactive state bound to a storage key.
function persisted<T>(
key: string,
fallback: T,
behavior?: CustomBehavior<T>
): PersistedState<T>;
key
The storage key used to persist the value.
fallback
The value used when no stored value exists or parsing fails.
behavior (optional)
Configuration controlling serialization, storage backend, synchronization,
and persistence timing.
PersistedState<T>type PersistedState<T> = {
value: T;
persist(debounce?: number | false): void;
destroy(): void;
readonly key: string;
};
.valueThe reactive value.
state.value = newValue;
.persist(debounce?)Manually persists the current value to storage.
state.persist();
state.persist(300);
.valueonStringifyError.destroy()Cleans up all side effects and removes the stored value.
state.destroy();
The behavior object allows fine-grained control over persistence.
const state = persisted('user', null, {
storage: sessionStorage,
debounce: 250,
syncTabs: false
});
| Option | Type | Default | Description |
|---|---|---|---|
storage |
Storage |
localStorage |
Storage backend |
parse |
(string) => T |
JSON.parse |
Deserialize stored value |
stringify |
(T) => string |
JSON.stringify |
Serialize value |
onParseError |
(unknown) => void |
remove item | Called when parsing fails |
onStringifyError |
(unknown) => void |
no-op | Called when serialization fails |
debounce |
number | false |
false |
Debounce persistence on value changes |
persistOnValueChange |
boolean |
false |
Persist when value changes |
persistOnTabChange |
boolean |
true |
Persist when tab becomes hidden |
syncTabs |
boolean |
true |
Sync value across tabs |
Debouncing applies only to persistence triggered by value changes.
const search = persisted('search', '', {
debounce: 300
});
.persist() calls are never debouncedWhen syncTabs is enabled (default):
storage eventCalling persisted() multiple times with the same key and storage
returns the same instance:
persisted('theme', 'light') === persisted('theme', 'light');
// true
This prevents duplicate listeners and keeps state consistent.
MIT