Zustand logo

Zustand

A small, fast, and scalable bearbones state-management solution using simplified flux principles.

npm install zustand
55.9K12.6M/weekv5.0.890.28 KBMIT7 issues
Last updated: 2025-08-20
Star history chart for pmndrs/zustand

TL;DR

A minimalist state management library that creates a global store using hooks, eliminating the need for boilerplate code or Context Providers.

It addresses common issues like the 'zombie child' problem and React concurrency while remaining unopinionated and extremely lightweight (<2kB).

Why Zustand?

Zustand has rapidly become the default choice for developers seeking the predictability of a global store without the verbosity of Redux. Its "bearbones" philosophy focuses on developer experience and performance.

  • Zero Boilerplate: Define your store, actions, and state in a single hook creation call. There are no reducers, dispatchers, or action types to maintain.
  • No Provider Wrapper: Unlike Context API or Redux, Zustand does not require wrapping your application in a Provider. This keeps your component tree clean and eliminates "Provider Hell."
  • Selective Re-rendering: Components automatically subscribe only to the specific slice of state they select. If other parts of the store update, your component does not re-render.
  • Transient Updates: The store can be accessed and modified outside of React components (e.g., inside utility functions or non-React event handlers) via the getState and setState API.
  • Middleware Support: It comes with built-in middleware for common needs like persisting state to localStorage (persist) or connecting to Redux DevTools.

Code Snippet

Zustand uses a hook generator create. You pass it a callback that receives set (to update state) and get (to read state).

import { create } from 'zustand'

// Create the store
const useStore = create((set) => ({
  bears: 0,
  // Simple state update
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  // Clear all state
  removeAllBears: () => set({ bears: 0 }),
  // Async action
  updateBearsAsync: async () => {
    const response = await fetch('/bear-api')
    const bears = await response.json()
    set({ bears })
  }
}))

// Consume in a component
function BearCounter() {
  // Select only what you need to prevent unnecessary re-renders
  const bears = useStore((state) => state.bears)
  return <h1>{bears} around here...</h1>
}

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

In this example, BearCounter will only re-render when the bears number changes. It is completely decoupled from Controls.

Pros and Cons

No library is perfect; understanding the trade-offs is key to selecting the right tool.

Pros

  • Simplicity & Low Learning Curve: A senior developer can read the documentation and migrate a feature to Zustand in under an hour.
  • Performance by Default: Selectors ensure that components are not over-rendering, a common performance pitfall with the native React Context API.
  • Unhindered Access: The ability to import the store and call useStore.getState() in vanilla JavaScript files is a massive architectural advantage for complex apps.

Cons

  • SSR Complexity: In frameworks like Next.js, using a global singleton store can lead to state pollution between requests. You must follow specific patterns (creating stores per request) to handle SSR safely.
  • Structural Freedom: Because it is unopinionated, large teams might create messy stores if they don't enforce their own architectural patterns (e.g., separating actions from state).
  • Documentation Fragmentation: While the core docs are good, finding advanced patterns for edge cases often requires searching through GitHub issues or discussions.

Comparison with Other State Management Libraries

The table below outlines the positioning differences between Zustand and other popular state management libraries to help you make an informed decision:

LibraryDesign PhilosophyBest ForPain Points
ZustandSimplified Flux
Centralized store, top-down updates, minimal boilerplate.
General Purpose
Mid-to-large apps needing global state without Redux complexity.
SSR Setup
Requires careful setup in Next.js to avoid cross-request state leakage.
Redux ToolkitStrict & Robust
Event-driven, immutable, highly structured flux architecture.
Enterprise Apps
Teams needing strict patterns, heavy debugging, and long-term maintainability.
Boilerplate
Even with Toolkit, it requires more code and setup than alternatives.
JotaiAtomic
Decentralized state (atoms), bottom-up composition.
Complex UI Logic
Apps with high interactivity and dependent state chains (like dashboards).
Visualization
Visualizing the dependency graph of atoms can be harder than a single state tree.

Ecosystem & Extensions

Zustand is designed to be extended. Here are the most common middleware patterns used in production:

  • Persist Middleware: Automatically saves your store to localStorage, sessionStorage, or AsyncStorage (React Native) and rehydrates it on load.
  • Immer Middleware: Allows you to write mutable code (e.g., state.nested.field = 1) inside your set functions, which is helpful for deeply nested state objects.
  • Redux DevTools: Connects your Zustand store to the Redux DevTools browser extension for time-travel debugging and state inspection.