API
Options and return values for persistedSignal and usePersistedSignal.
API#
A quick reference for the main functions, options, and runtime behavior.
Recommended order: choose
storagefirst, then addsync, custom serialization, cookie options, or IndexedDB config only if needed.
Functions#
persistedSignal<T>(key: string, initialValue: T, options?: PersistedSignalOptions<T>): PersistedSignal<T>
usePersistedSignal<T>(key: string, initialValue: T, options?: PersistedSignalOptions<T>): PersistedSignal<T>
createPersistedModel<TModel, TSnapshot, TArgs>(options, factory): ModelConstructor<TModel & PersistedModelControls, TArgs>
createPersistedScope(options?): PersistedScope
dehydratePersisted(scope): PersistedDehydratedState
hydratePersisted(state): void
serializePersistedStateForHtml(state): string
createBroadcastSyncTransport(channel): PersistedSyncTransport | null
createPersistedEventTarget(options?): PersistedEventTarget
- Use
persistedSignal()for shared signals across modules. - Use
usePersistedSignal()inside components. - Use
createPersistedModel()for a typed, explicitly selected persisted snapshot of a Preact Signals model. - Use persisted scopes to isolate SSR requests and dehydrate/hydrate state safely.
Quick examples#
const theme = persistedSignal("theme", "dark", { storage: "local" });
const sidebarOpen = usePersistedSignal("sidebar-open", true, { storage: "session" });
Options#
type PersistedSignalOptions<T> = {
storage?: "local" | "session" | "indexeddb" | "cookie" | "memory";
serialize?: (value: T) => string;
deserialize?: (raw: string) => T;
sync?: boolean;
removeOnUndefined?: boolean;
indexedDB?: {
database?: string;
store?: string;
version?: number;
};
version?: number;
migrate?: (snapshot: unknown, fromVersion: number) => T | Promise<T>;
validate?: (snapshot: unknown) => snapshot is T;
migrationErrorStrategy?: "preserve" | "reset" | "throw";
legacyVersion?: number;
scope?: PersistedScope;
cookie?: {
expires?: number | Date;
path?: string;
domain?: string;
secure?: boolean;
sameSite?: "Lax" | "Strict" | "None";
};
cookieContext?: CookieContext;
};
Most important options#
| Option | Purpose | Typical use |
|---|---|---|
storage |
selects the backing store | local, session, cookie, indexeddb |
serialize / deserialize |
customize stored format | complex values or compatibility with existing data |
sync |
keep controllers in sync | disable only when inbound updates are not wanted |
removeOnUndefined |
remove persisted value when signal becomes undefined |
optional values |
indexedDB |
IndexedDB-specific settings | multi-db or multi-store setups |
version / migrate / validate |
evolve persisted payloads safely | schema changes |
scope |
request-local SSR collection and hydration | Preact SSR, Hono, edge runtimes |
sync: "tabs" |
opt-in persisted model cross-tab sync | preferences shared across tabs |
events |
lifecycle observability | logging, metrics, devtools plugins |
cookie |
cookie-specific settings | SSR-visible values |
cookieContext |
server-aware cookie access during SSR | Astro, Fresh, middleware, SSR routes |
Returned signal#
type PersistedSignal<T> = Signal<T> & {
clear(): void;
reset(): void;
}
Methods#
clear()removes the persisted value and restoresinitialValue.reset()restoresinitialValueand persists it again.
Runtime rules#
- Browser-only storage is never touched on the server.
local,session, andindexeddbfall back to memory when unavailable.- IndexedDB hydration is async, so the signal starts with
initialValueand updates after loading. persistedSignal()is global by identity.- Reusing the same global identity requires the same effective options; conflicting options throw.
- For SSR cookie usage, different
cookieContextvalues stay isolated.
Important behavior notes#
Global persistedSignal() reuse#
These calls reuse the same signal:
persistedSignal("theme", "dark", { storage: "local" });
persistedSignal("theme", "light", { storage: "local" });
But if you reuse the same identity with conflicting options, an error is thrown:
persistedSignal("theme", "dark", { storage: "local", sync: true });
persistedSignal("theme", "dark", { storage: "local", sync: false }); // throws
IndexedDB hydration#
IndexedDB-backed signals do not block render. They start with initialValue and update after the stored value loads.
Cookie SSR#
Use cookieContext only when the server must read or write the cookie during SSR.
Model APIs#
@kamod-ch/signals also re-exports official Preact Signals model primitives:
signal
computed
effect
batch
untracked
action
createModel
useModel
See Models and actions, Persisted models, and Versioning and migrations.