Persist
Sync state with localStorage, sessionStorage, or cookies.
Persist
The persist middleware automatically saves and restores store state across browser sessions. It provides a bridge between your in-memory store and long-term storage targets.
Usage
You can use persist either directly on a store object or as a curried middleware factory during store creation.
import { persist } from '@ilokesto/state/middleware';
const store = persist({ local: 'counter-storage' })({ count: 0 });Storage Configuration
The middleware supports three primary storage targets through the configuration object:
local: (string) The key name forlocalStorage. Use this for data that should survive browser restarts.session: (string) The key name forsessionStorage. Use this for data that should be cleared when the tab is closed.cookie: (string) The key name for cookie storage. This is useful for sharing small pieces of state with the server.
The Rehydration Flow
When a store is initialized with persist, it follows a specific setup sequence:
- Initial Setup: The store is created with the provided initial state.
- Synchronous Read: The middleware immediately attempts to read the persisted state from the configured storage.
- Replacement: If valid data is found, that rehydrated state becomes the store's current state.
- Referential-Change Guard: The store reference is updated only if the persisted state differs from the initial state.
Storage Shape and Versioning
Data is saved in a wrapper object:
{
"state": { ... },
"version": 2
}- Version derivation: In the current model, the
versionis derived from the length of yourmigratechain. If you have 3 migration functions, the version is 3. - Write-Through Behavior: After every store commit, the middleware writes the new state to storage.
- Write Cache: To avoid redundant I/O, the middleware caches the encoded persisted payload and skips writes when the next encoded value is identical.
Migrations
The migrate option accepts an array of functions to transform old state shapes into the current one.
- Target limitations: Currently, migrations are only supported for
localandcookietargets.sessionstorage does not support the migration flow. - Incremental updates: Each function in the array represents one version step. If the stored version is 1 and the current chain length is 3, functions
[1]and[2]will be applied sequentially.
Cookie Limitations
When using the cookie target, keep in mind:
- Size: Cookies are typically limited to 4KB. Large state trees will fail to persist.
- Serialization: Data is JSON-serialized. Ensure your state does not contain circular references or non-serializable objects (like Functions or Classes).
Middleware Ordering
The position of persist() in your middleware chain matters.
Recommended order: Place persist() towards the end of the chain.
This ensures that it only saves the "final" state after other middleware (like validate or debounce) have had their chance to process the update.
const store = pipe(
validate(schema),
persist({ local: 'app-state' })
)({ count: 0 });