Skip to content
masterclassBuddyTech Buzz5 fresh blogs · 5:00 AM IST

Zustand · Function

persist

The persist middleware saves Zustand store state across page reloads or application restarts. It supports custom storage, selective persistence, version migration, merging, and controlled rehydration.

How it works

Wrap a Zustand state creator with persist to save selected store state in a named storage entry and restore it during rehydration. By default, JSON-backed localStorage is used; options can select storage, filter fields, migrate versions, customize merging, observe rehydration, or defer hydration.

Syntax

persist(stateCreatorFn, persistOptions)

Parameters

NameTypeDescription
stateCreatorFnStateCreatorFunction receiving set, get, and store; normally returns the store's exposed state and methods.
persistOptionsPersistOptionsConfiguration object for persistence behavior.
namestringUnique storage key for the persisted store.
storagePersistStorageOptional PersistStorage used to read and write persisted state; defaults to JSON localStorage storage.
partialize(state) => partial stateOptional function selecting fields to persist.
onRehydrateStorage(state) => void or callbackOptional callback for logic before and after rehydration.
versionnumberOptional persisted-state version number.
migrate(persistedState, version) => stateOptional transformer used when the stored version differs.
merge(persistedState, currentState) => stateOptional custom merge function; shallow merge is the default.
skipHydrationbooleanOptional flag that disables automatic initialization rehydration.

Returns

A state creator enhanced with persistence and rehydration behavior; the resulting store exposes persistence interactions such as rehydrate.

Key points

  • It keeps store data available beyond the current page lifetime by saving and restoring state.
  • The name identifies the persisted entry and should not collide with another store's entry.
  • Use partialize when only a subset of the in-memory state belongs in storage.
  • Versioned stores can transform older serialized data through migrate before using it.
  • Override the default shallow merge when restoring nested or otherwise specially combined state.
  • Use the rehydration callback to run logic around restoration, such as tracking completion or errors.

Common mistakes

  • Setting skipHydration to true disables automatic initialization rehydration; call rehydrate manually when the application is ready.
  • Changing version without supplying compatible migration logic causes persisted data with the old version to be ignored.
  • Use the lazy storage getter to avoid accessing browser storage during module evaluation, especially in server-side rendering.

Example: Persist a counter store

import { create } from 'zustand'
import { persist } from 'zustand/middleware'

type CounterState = {
  count: number
  increment: () => void
}

export const useCounterStore = create<CounterState>()(
  persist(
    (set) => ({
      count: 0,
      increment: () => set((state) => ({ count: state.count + 1 })),
    }),
    { name: 'counter-storage' },
  ),
)

The counter is restored from storage after a page reload.

When to use it

Persist browser state

A browser application needs user preferences or session-like UI state to survive refreshes.

Persist a selected subset

A store contains transient fields alongside durable data.

Migrate stored versions

The serialized store shape changes between releases.

Control SSR hydration

An SSR application must delay browser-storage access and hydration.

Interview questions

QWhat does persist do and what are its required inputs?

persist accepts a state creator and persistence options. The options require a unique name and can configure storage, filtering, migration, merging, versioning, hydration callbacks, and manual hydration.

Follow-up: How would you persist only part of a store? Provide partialize, a function that returns the fields to write.

QWhat storage does persist use by default?

The default storage is created with createJSONStorage(() => localStorage). The getter is lazy, so storage is accessed only when persistence first needs it.

Follow-up: Why is that useful in SSR? It avoids failing while the module is evaluated in an environment without localStorage.

QHow can hydration be controlled manually?

Set skipHydration to true when initialization should not automatically restore persisted state. Later, call the store's rehydrate function explicitly.

Follow-up: What is the default behavior if skipHydration is omitted? Automatic rehydration occurs because the default is false.

  • createJSONStorage
  • PersistStorage
  • rehydrate
  • partialize
  • migrate
  • merge
  • onRehydrateStorage

Source: persist. Checked 2026-10-15.