Skip to content
D
Documentation

Build a React store

how-to
1 min readUpdated

Use this page when a React component needs shared state, colocated actions, and a visible update. You create a hook with create, select the state or action you need, and render the selected value.

Prerequisites

Use React 18 or later, TypeScript in strict mode, and Zustand 5.0.15. Install Zustand in your React project:

bash
npm install zustand

Build and mount the store

Define state and its action together, then read the same store from separate mounted components. The page renders the current count and an Increment button; clicking the button updates every component that selects the count.

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

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

const useCounterStore = create<CounterStore>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}))

function CounterValue() {
  const count = useCounterStore((state) => state.count)

  return <p>Count: {count}</p>
}

function CounterControls() {
  const increment = useCounterStore((state) => state.increment)

  return (
    <button type="button" onClick={increment}>
      Increment
    </button>
  )
}

function Counter() {
  return (
    <main>
      <CounterValue />
      <CounterControls />
    </main>
  )
}

const rootElement = document.getElementById('root')

if (!rootElement) {
  throw new Error('The root element is missing')
}

createRoot(rootElement).render(<Counter />)
The mounted counter displays Count: 0 and an Increment button in separate components.

The initial render shows Count: 0 and the Increment button. The two components share the hook, so each click calls the colocated action and updates the value component.

See the live demo or its React example source for a complete application using the same store shape.

Choose selectors for the values you use

For selector granularity, equality, and stable computed results, see Selectors and rendering.

Keep updates immutable. Zustand shallow-merges the object returned by set by default, but nested objects need an explicit nested merge. Passing the replace flag replaces the complete state model, including actions, so use that form only when replacing the whole store is intentional. See Immutable state updates.

Where to go next

Was this page helpful?

Build a React store — zustand · GPT-5.6 Luna