Svelte utilities for synchronizing state with external systems.
This package provides utilities that help synchronize Svelte state with external data sources like local storage, remote APIs, or other browser APIs. These utilities handle the complexity of keeping state in sync across multiple sources while maintaining Svelte's reactive paradigm.
This package is part of the Epicenter monorepo and is used internally. To use it in your app:
{
"dependencies": {
"@epicenter/svelte": "workspace:*"
}
}Creates a persisted state object tied to local storage, accessible through .value. This utility ensures your state survives page refreshes and synchronizes across browser tabs.
- Synchronous initialization: Immediate access to a valid value
- Automatic validation: Uses the provided schema to validate stored data
- Cross-tab synchronization: Syncs state across browser tabs via storage events
- Graceful error recovery: Falls back to defaults via
onParseErrorhandler - Type-safe: Full TypeScript support with inferred types
import { createPersistedState } from '@epicenter/svelte';
import { z } from 'zod';
// Define your schema
const settingsSchema = z.object({
theme: z.enum(['light', 'dark']),
notifications: z.boolean()
});
// Create persisted state
const settings = createPersistedState({
key: 'app-settings',
schema: settingsSchema,
onParseError: (error) => {
// Handle different error types
if (error.type === 'storage_empty') {
return { theme: 'light', notifications: true }; // default value
}
console.error('Settings parse error:', error);
return { theme: 'light', notifications: true }; // fallback value
}
});
// Use in component
$effect(() => {
console.log('Current theme:', settings.value.theme);
});
// Update settings
settings.value = { theme: 'dark', notifications: false };Note: For production use, consider wrapping the persisted state in an encapsulated pattern with controlled access methods. See Encapsulated State Pattern for best practices.
createPersistedState<TSchema extends StandardSchemaV1>(options: {
key: string;
schema: TSchema;
onParseError: (error: ParseErrorReason<TSchema>) => StandardSchemaV1.InferOutput<TSchema>;
onUpdateSuccess?: (newValue: StandardSchemaV1.InferOutput<TSchema>) => void;
onUpdateError?: (error: unknown) => void;
onUpdateSettled?: () => void;
})key: The key used to store the value in local storageschema: A Standard Schema v1 compatible schema for validationonParseError: Handler called when the value from storage cannot be parsed or validated. Must return a valid default value.onUpdateSuccess(optional): Called when the value is successfully written to storageonUpdateError(optional): Called when writing to storage failsonUpdateSettled(optional): Called after update attempt completes (success or failure)
The onParseError handler receives one of these error types:
storage_empty: No value found in storage for the given keyjson_parse_error: Failed to parse the stored JSON stringschema_validation_async_during_sync: Schema validation returned a Promise during synchronous parsingschema_validation_failed: Schema validation failed with specific issues
// Persist table sorting state
const sorting = createPersistedState({
key: 'data-table-sorting',
schema: z.array(z.object({
id: z.string(),
desc: z.boolean()
})),
onParseError: () => [{ id: 'created_at', desc: true }] // default sort
});
// Persist row selection
const rowSelection = createPersistedState({
key: 'data-table-selection',
schema: z.record(z.boolean()),
onParseError: () => ({}) // no selection by default
});When adding new utilities to this package:
- Add the utility file to
src/ - Export it from
src/index.ts - Update this README with documentation
- Add type tests if applicable
This package depends on:
svelte: For reactive state primitives@standard-schema/spec: For schema validation typeswellcrafted: For error handling utilities