Keep user preferences or UI position after reloads using localStorage.
Browser applications
Zustand · Pipe
The persist middleware saves Zustand state across page reloads or application restarts. Configure a unique storage key and optionally provide a custom storage adapter.
The persist middleware wraps a Zustand state creator and stores its state under a unique name. A storage adapter can be supplied explicitly; createJSONStorage converts a storage implementation such as localStorage into the format persist expects, and its lazy getter avoids accessing browser storage during module evaluation.
persist(stateCreatorFn, persistOptions)| Name | Type | Description |
|---|---|---|
stateCreatorFn | StateCreator | State creator function receiving set, get, and store; it returns the store state and actions. |
persistOptions | PersistOptions | Configuration object for persistence. |
name | string | Unique storage key for the persisted store. |
storage | PersistStorage | Optional PersistStorage adapter; defaults to createJSONStorage(() => localStorage). |
A Zustand store enhanced with persistence and hydration behavior.
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
const usePositionStore = create(
persist(
(set) => ({
position: { x: 0, y: 0 },
setPosition: (position) => set({ position }),
}),
{
name: 'position-storage',
storage: createJSONStorage(() => localStorage),
},
),
)
usePositionStore.getState().setPosition({ x: 10, y: 20 })Creates a store whose position state is serialized to localStorage and restored after a reload.
Browser applications
React Native applications
URL-driven interfaces
Server-rendered applications
persist wraps a state creator and saves the selected store data in a storage backend across reloads or restarts. The required name option identifies the stored item, while storage selects the adapter and defaults to JSON-backed localStorage.
Follow-up: How would you support a nonstandard backend? Supply a storage object, commonly produced with createJSONStorage, whose getter returns the desired storage implementation.
createJSONStorage accepts a function that returns a storage implementation and creates a compatible storage object. The function is evaluated lazily, so browser-only APIs are not accessed during module evaluation.
Follow-up: Why does that matter for SSR? Server execution may not provide localStorage, so deferred access prevents module-evaluation failures.
Hydration retrieves persisted state and merges it with the current state. Synchronous storage hydrates during store creation, whereas asynchronous storage hydrates later in a microtask.
Follow-up: What design concern follows from asynchronous hydration? UI or actions that depend on restored values may need to wait until hydration has completed.
createJSONStoragePersistStorageStateStoragerehydratepersisting store datalocalStorageAsyncStorage