Skip to main content
This guide will get you up and running with Zustand in just a few minutes. You’ll create a store, use it in components, and see how simple state management can be.

Your first store

Creating a store in Zustand is straightforward. Your store is a hook that you can use anywhere in your application.
1

Install Zustand

First, install Zustand in your project:
npm install zustand
2

Create a store

Import create from zustand and define your store. You can put anything in it: primitives, objects, functions.
store.js
import { create } from 'zustand'

const useBearStore = create((set) => ({
  bears: 0,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
}))
The set function merges state by default. You can update just the properties you want to change.
3

Use the store in components

Use the hook anywhere in your app. No providers needed! Components will re-render when the selected state changes.
BearCounter.jsx
function BearCounter() {
  const bears = useBearStore((state) => state.bears)
  return <h1>{bears} around here...</h1>
}

function Controls() {
  const increasePopulation = useBearStore((state) => state.increasePopulation)
  return <button onClick={increasePopulation}>one up</button>
}

Complete example

Here’s a complete working example you can copy and run:
App.jsx
import { create } from 'zustand'

// Create your store
const useStore = create((set) => ({
  count: 1,
  inc: () => set((state) => ({ count: state.count + 1 })),
}))

// Use it in your components
function Counter() {
  const { count, inc } = useStore()
  return (
    <div>
      <span>{count}</span>
      <button onClick={inc}>one up</button>
    </div>
  )
}

export default Counter

Understanding selectors

Selectors are functions that pick specific values from your store. They’re crucial for performance:
// ✅ Good: Only re-renders when bears changes
const bears = useBearStore((state) => state.bears)

// ✅ Good: Only re-renders when increasePopulation changes (never, it's a function)
const increasePopulation = useBearStore((state) => state.increasePopulation)

// ⚠️ Careful: Re-renders on ANY state change
const state = useBearStore()
Fetching the entire state without a selector will cause your component to re-render on every state change, even if the component only uses a small part of the state.

Updating state

There are multiple ways to update state in Zustand:

Partial updates

The set function merges state by default:
const useStore = create((set) => ({
  firstName: 'John',
  lastName: 'Doe',
  updateFirstName: (name) => set({ firstName: name })
  // lastName remains unchanged
}))

Functional updates

When the new state depends on the previous state, use a function:
const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  incrementBy: (amount) => set((state) => ({ count: state.count + amount })),
}))

Complete replacement

Pass true as the second argument to replace the entire state:
const useStore = create((set) => ({
  count: 0,
  reset: () => set({ count: 0 }, true) // Replaces entire state
}))
Be careful with complete replacement - it will remove all state properties, including your action functions!

Async actions

Async actions work naturally - just call set when you’re ready:
const useStore = create((set) => ({
  fishies: {},
  fetch: async (pond) => {
    const response = await fetch(pond)
    set({ fishies: await response.json() })
  },
}))

Reading state in actions

Use the get parameter to read the current state inside actions:
const useStore = create((set, get) => ({
  sound: 'grunt',
  action: () => {
    const sound = get().sound
    // Use the current sound value
    console.log(sound)
  },
}))

Multiple state slices

You can select multiple values efficiently:
// Multiple separate selectors (recommended)
const nuts = useBearStore((state) => state.nuts)
const honey = useBearStore((state) => state.honey)

// Or use useShallow for object selectors
import { useShallow } from 'zustand/react/shallow'

const { nuts, honey } = useBearStore(
  useShallow((state) => ({ nuts: state.nuts, honey: state.honey }))
)

Using state outside components

You can read and update state outside React components:
const useDogStore = create(() => ({ paw: true, snout: true, fur: true }))

// Get current state
const paw = useDogStore.getState().paw

// Update state
useDogStore.setState({ paw: false })

// Subscribe to changes
const unsub = useDogStore.subscribe(console.log)

// Unsubscribe
unsub()
This is useful for integrating with non-React code, logging, or creating side effects.

Common patterns

Separate actions from state

Keep your store organized by grouping related logic:
const useStore = create((set, get) => ({
  // State
  count: 0,
  user: null,
  
  // Actions
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  setUser: (user) => set({ user }),
}))

Computed values

Create derived state with selectors:
const useStore = create((set) => ({
  firstName: 'John',
  lastName: 'Doe',
}))

// In your component
const fullName = useStore(
  (state) => `${state.firstName} ${state.lastName}`
)

Resetting state

Store the initial state and reset to it:
const initialState = {
  bears: 0,
  fish: 0,
}

const useStore = create((set) => ({
  ...initialState,
  increase: () => set((state) => ({ bears: state.bears + 1 })),
  reset: () => set(initialState),
}))

Next steps

You now know the basics of Zustand! Here’s where to go next:

Comparison

See how Zustand compares to Redux, Jotai, and other state managers

TypeScript guide

Learn TypeScript best practices with Zustand

Middleware

Add persistence, devtools, and more with middleware

API reference

Explore the complete API documentation

Build docs developers (and LLMs) love