# Merge partial persisted state

Use a custom `merge` function when persisted data contains only part of a nested object and hydration must keep the fields already present in the current store.

Build the store with [`create`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand#create) and wrap its state creator with [`persist`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand-middleware#persist).

## When to use it

`persist` hydrates the store by merging the stored value with the current state. Its default merge is shallow: if storage contains `settings: { theme: 'dark' }`, it replaces the complete current `settings` object, including fields such as `density`. Supply a nested merge when the persisted object is partial.

## Example

This complete React example hydrates a partial `settings` object and keeps the current `density` field.

```tsx
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
import { createRoot } from 'react-dom/client'

type Settings = {
  theme: 'light' | 'dark'
  density: 'compact' | 'comfortable'
}

type Store = {
  settings: Settings
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null
}

function getTheme(value: unknown): Settings['theme'] | undefined {
  if (!isRecord(value) || !isRecord(value.settings)) return undefined
  return value.settings.theme === 'light' || value.settings.theme === 'dark'
    ? value.settings.theme
    : undefined
}

localStorage.setItem(
  'settings',
  JSON.stringify({ state: { settings: { theme: 'dark' } }, version: 0 }),
)

const useStore = create<Store>()(
  persist(
    () => ({ settings: { theme: 'light', density: 'comfortable' } }),
    {
      name: 'settings',
      merge: (persistedState, currentState) => {
        const theme = getTheme(persistedState)
        return {
          ...currentState,
          settings: {
            ...currentState.settings,
            ...(theme === undefined ? {} : { theme }),
          },
        }
      },
    },
  ),
)

function SettingsView() {
  const settings = useStore((state) => state.settings)
  return <p>{settings.theme} / {settings.density}</p>
}

const root = document.getElementById('root')
if (root !== null) createRoot(root).render(<SettingsView />)
```

After hydration, the mounted component displays `dark / comfortable`: the persisted theme applies and the current density remains.

## Options that matter here

| Option | Type | Default | What it does |
|---|---|---|---|
| `name` | `string` | — | Names the storage entry. Use a unique name for the store. |
| `partialize` | `(state: State) => Object` | `(state) => state` | Selects the fields written to storage. |
| `merge` | `(persistedState: unknown, currentState: State) => State` | Shallow merge | Combines stored data with current state during hydration. |

## Pitfalls

- A custom nested merge is needed when partial persistence targets nested objects. The default shallow merge can erase fields that are present only in the current nested object.
- Async persisted storage hydrates after the initial render, so the UI can show default state temporarily. Wait for hydration when that timing matters; see [Control persisted hydration](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/control-persisted-hydration).

## Live demo

Explore the [Zustand demo](https://zustand-demo.pmnd.rs/) for a mounted store application.

## Related

- [Persist store data](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/persist-store-data)
- [Persist selected state](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/persist-selected-state)
- [Migrate persisted state](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/migrate-persisted-state)
