# Persist store data

Use [`persist`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand-middleware#persist) when a store's data needs to survive a page reload. Wrap your state creator, choose a unique storage key, and use `partialize` to select the fields to save.

The browser example below keeps a bear count across reloads while a temporary click count resets to zero. It uses React and the browser's `localStorage`.

## 1. Create a persisted store

In your React TypeScript project, install Zustand:

```bash
npm install zustand
```

Pass the wrapped state creator to [`create`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand#create). `persist` returns a state creator; `create` returns the hook your components use. Use [`createJSONStorage`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand-middleware#createjsonstorage) to adapt browser storage to JSON reads and writes.

```ts title="store.ts"
import { create } from 'zustand'
import { createJSONStorage, persist } from 'zustand/middleware'

type BearStore = {
  bears: number
  temporaryClicks: number
  addABear: () => void
}

export const useBearStore = create<BearStore>()(
  persist(
    (set) => ({
      bears: 0,
      temporaryClicks: 0,
      addABear: () =>
        set((state) => ({
          bears: state.bears + 1,
          temporaryClicks: state.temporaryClicks + 1,
        })),
    }),
    {
      name: 'bear-counter-storage',
      storage: createJSONStorage(() => localStorage),
      partialize: (state) => ({ bears: state.bears }),
    },
  ),
)
```

`name` is the storage key and the only required option. Give each persisted store a unique key so stores do not share the same storage entry.

`partialize` picks the data written to storage, not the fields available to components. Here, the live store still contains `temporaryClicks` and `addABear`, but the persisted state contains only `bears`. Each action update writes the selected state under `bear-counter-storage`.

## 2. Render and test the counter

Select the values and action separately, following the React starter's counter pattern. Use this file as your browser entry point:

```tsx title="main.tsx"
import { createRoot } from 'react-dom/client'
import { useBearStore } from './store'

function App() {
  const bears = useBearStore((state) => state.bears)
  const temporaryClicks = useBearStore((state) => state.temporaryClicks)
  const addABear = useBearStore((state) => state.addABear)

  return (
    <main style={{ minHeight: 400 }}>
      <h1>Persisted bear counter</h1>
      <p>Bears: {bears}</p>
      <p>Clicks since reload: {temporaryClicks}</p>
      <button onClick={addABear}>Add a bear</button>
    </main>
  )
}

createRoot(document.getElementById('root')!).render(<App />)
```

Use the HTML setup from [React quick start](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/react-quick-start#3-mount-the-counter), with the script's `src` set to `/main.tsx` to load the entry point above.

Click **Add a bear**: both counts increase. Reload the page: the bear count returns from storage, and **Clicks since reload** shows zero. Rehydration merges the saved fields into the initial store, retaining the action and the initial value for the omitted field.

## 3. Choose local or session storage

For `localStorage`, keep the sample's `storage` option, or omit it to use the default adapter. For `sessionStorage`, replace that option with `storage: createJSONStorage(() => sessionStorage)`. The same counter and `partialize` function work with either storage engine.

Both browser storage engines are synchronous, so Zustand hydrates during store creation. Use `sessionStorage` for data scoped to the browser page session; use `localStorage` when the data needs to remain after that session ends.

## Options that matter

The types below describe the sample's full `BearStore` and selected persisted state. A storage adapter implements [`PersistStorage`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand-middleware#persiststorage).

| Option | Type | Default | What it does |
| --- | --- | --- | --- |
| `name` | `string` | Required; no default | Sets the unique storage key. |
| `storage` | `PersistStorage<{ bears: number }>` | `createJSONStorage(() => localStorage)` | Selects the storage adapter used to read and write state. |
| `partialize` | `(state: BearStore) => { bears: number }` | `(state) => state` | Filters the state before each write. |

## Pitfalls

Asynchronous storage does not hydrate before the initial render. Defaults can therefore look like persisted values—for example, a logged-out state—while storage is still loading. Track hydration completion and wait before rendering UI that depends on persisted data. See [Control persistence hydration](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/control-persistence-hydration) for the hydration gate.

For partially persisted nested objects and custom merging, see [Migrate persisted state](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/migrate-persisted-state). For server-rendered React applications, see [Next.js store setup](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/nextjs-store-setup).

## Live demo and related

Try the [live Zustand demo](https://zustand-demo.pmnd.rs/) for the hook-based counter interaction. The example on this page adds persistence to that pattern.

- [State and actions](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/state-and-actions)
- [Type stores and middleware](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/type-stores-and-middleware)
- [Control persistence hydration](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/control-persistence-hydration)
