Open-source project
pmndrs/zustand avatar
pmndrs/zustand

Zustand: A Small React State Store That Sidesteps Providers and Boilerplate

Bear necessities for state management in React. If you want to construct a single object with multiple state-picks inside, similar to redux's mapStateToProps, you can use useShallow to prevent unnecessary rerenders when the selector output does not change according to shallow equal.

58,679 stars2,187 forksTypeScriptMIT

At a glance

What is it?
Zustand is a hooks-based state management library for React that uses simplified flux principles without context providers. It handles concurrency and zombie children well, but its flexibility comes with trade-offs in structure and server-side caution.
Who is it for?
Zustand is for React developers who want a minimal, unopinionated store that works with hooks and avoids provider nesting, especially in client-side apps where transient updates and external subscriptions matter. It is not the right tool if you need strong architectural guardrails, if you are building React Server Components (where external store access is risky), or if you prefer enforced action patterns.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 14, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What Zustand Solves and Who It Serves

Zustand addresses a specific pain: React state that needs to be shared across components without the ceremony of context providers, reducers, or action creators. It targets developers who find Redux too verbose and Context too prone to re-rendering entire trees. The README pitches it as a 'small, fast and scalable bearbones state-management solution using simplified flux principles' with a 'comfy API based on hooks.' It is not opinionated: you can put primitives, objects, or functions in the store, and updates are immutable via a `set` function that merges state by default. The primary audience is React developers building client-side apps who want a central store accessible from any component without wrapping the app in providers.

Core Mechanism: Store as Hook, No Providers

The store is created with `create`, and it returns a hook. You call that hook with a selector function, and the component re-renders only when the selected slice changes. For example, `const bears = useBearStore((state) => state.bears)` subscribes to just `bears`. The store also exposes `getState`, `setState`, and `subscribe` on the hook's prototype, allowing external, non-reactive access. This design avoids context entirely: no provider components, no nesting. The README stresses that zustand handles 'the dreaded zombie child problem, react concurrency, and context loss between mixed renderers,' which are classic pitfalls in React state management. The mechanism relies on strict equality (old === new) by default, which is efficient for atomic picks. For multiple slices, `useShallow` performs a shallow equal comparison to avoid unnecessary re-renders when the selector output is a new object or array but its contents are the same.

Getting Started: Commands and Core API

Installation is one command: `npm install zustand`. From there, you create a store with `create`, passing a function that receives `set` and optionally `get`. The README shows a simple bear counter: `const useBearStore = create((set) => ({ bears: 0, increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), removeAllBears: () => set({ bears: 0 }) }))`. Components then use the hook directly, like `const bears = useBearStore((state) => state.bears)`. For multiple slices, you can use `useShallow` from `zustand/react/shallow` to wrap a selector that returns an object or array. For custom equality, you need `createWithEqualityFn` and pass a comparison function as the second argument to the hook. The `set` function has a second boolean argument: `false` (default) merges state, `true` replaces the entire state. Async actions are just functions that call `set` when data arrives, and you can read state outside components via `getState()`.

Transient Updates and External Subscriptions

One notable feature is the ability to inform components transiently without causing a render. The README mentions this as an advantage over Redux, though it does not show the exact API. However, the `subscribe` method is documented: `useDogStore.subscribe(console.log)` fires synchronously on every state change, and you can unsubscribe with the returned function. For selective subscription, the `subscribeWithSelector` middleware extends `subscribe` to accept a selector, callback, and options like `equalityFn` and `fireImmediately`. This is useful for reacting to state changes outside React's render cycle, such as in event handlers or external modules. The trade-off is that these external subscriptions bypass React's batching, so you must manage listener cleanup yourself, and the README warns against using this technique in React Server Components due to potential bugs and privacy issues.

Limitations and Wrong Use Cases

Zustand's unopinionated nature is a double-edged sword. There is no built-in action pattern, so you can easily scatter state mutations across components, making large apps harder to reason about. The README warns that fetching everything with `useBearStore()` causes the component to update on every state change, which is a footgun. Overwriting state with `set({}, true)` can wipe out actions, as the example shows, and the README explicitly cautions to 'be careful not to wipe out parts you rely on.' For React Server Components, the external `getState` and `subscribe` methods are not recommended; the README links to a discussion (#2200) about unexpected bugs and privacy issues. Also, the default merge behavior can lead to accidental nested state overwrites if you are not careful with immutability. Zustand is not a good fit if you need strict architectural enforcement or if you are building server-rendered apps where external store access is risky.

Alternative: Redux and Context Differences

The README directly compares zustand to Redux and Context. Against Redux, zustand is simpler and un-opinionated, uses hooks as the primary consumption method, does not wrap your app in context providers, and can inform components transiently. Redux, by contrast, enforces a structured flow with actions, reducers, and a single store, which can be overkill for small to medium apps but provides discipline for large teams. Against Context, zustand has less boilerplate, renders components only on changes (Context re-renders all consumers when the context value changes, even if they pick a slice), and centralizes state with action-based management. Context is built into React and works fine for low-frequency updates like theme or user, but for high-frequency state, zustand's selective subscriptions are more efficient. The key difference is approach: zustand uses external store subscriptions with hooks, while Context uses React's built-in propagation, which is less granular.

Maintenance, Licensing, and Upgrade Path

The project is actively maintained, with recent releases in 2026 (v5.0.15, v5.0.14, v5.0.13) and a default branch of `main`. The license is MIT, which permits commercial use, modification, and distribution with attribution, though this is not legal advice. The README references a migration guide for v5, specifically for using custom equality functions like `createWithEqualityFn`, indicating that the API changed between major versions. Upgrading from v4 to v5 requires checking the migration notes, especially if you used custom equality functions or relied on the default `shallow` behavior. The README also mentions a TypeScript usage section, which is important for type safety but not fully shown here. Maintenance cost is low for basic usage, but you must stay current with releases to get fixes for concurrency and zombie child issues, which the project explicitly claims to handle.

Editorial conclusion

Zustand is for React developers who want a minimal, unopinionated store that works with hooks and avoids provider nesting, especially in client-side apps where transient updates and external subscriptions matter. It is not the right tool if you need strong architectural guardrails, if you are building React Server Components (where external store access is risky), or if you prefer enforced action patterns. Before adopting, verify your React version compatibility, review the migration notes for v5 if upgrading, and test how your selector equality functions behave with useShallow to avoid re-render surprises.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Community notes

Community notes