What is Zustand? Simple, Fast and Scalable State Management
Near-zero-boilerplate global state for React with Zustand. Stores, selectors, async actions, persist middleware and full TypeScript support.
Zustand is a small, fast and scalable global state management library for React applications. It needs no provider, ships with far less boilerplate than Redux, and has excellent TypeScript support out of the box. Selectors prevent unnecessary re-renders explicitly; persist middleware lets you write state straight to localStorage. This guide expands the infographic to cover everything from basic usage to advanced patterns.
What is Zustand?
A small, fast and scalable state management library for React. Without reducer/action/dispatch boilerplate, you call a hook and get state plus setters.
- Less boilerplate
- Minimal setup
- Built-in React support
- Works for both small and large apps
Why Zustand?
What Zustand brings to the table:
- No provider required
- Minimal API
- Built-in TypeScript support
- Compatible with React Native APIs
- Much smaller footprint than Redux
Installation
Single package — installed as a runtime dependency.
npm install zustandCreate a Store (Core Idea)
create takes a factory that receives set and returns state and actions. The result is itself a hook.
import { create } from "zustand";
type CounterState = {
count: number;
increment: () => void;
decrement: () => void;
};
export const useCounter = create<CounterState>((set) => ({
count: 0,
increment: () => set((s) => ({ count: s.count + 1 })),
decrement: () => set((s) => ({ count: s.count - 1 })),
}));Using the Store
The store is a hook. Read values and actions by calling it — no need to wrap your component in anything.
function Counter() {
const { count, increment } = useCounter();
return (
<button onClick={increment}>
Count: {count}
</button>
);
}Selectors (IMPORTANT)
Pass a selector to subscribe only to the slice you need. Your component re-renders only when that slice changes — critical for performance.
const count = useCounter((s) => s.count);
const increment = useCounter((s) => s.increment);Async Actions
Define async functions inside the store and call set as needed. Data fetching, login flows and similar async work fit naturally.
export const useUsers = create((set) => ({
users: [],
loading: false,
fetchUsers: async () => {
set({ loading: true });
const res = await fetch("/api/users");
const users = await res.json();
set({ users, loading: false });
},
}));Persist Middleware
The persist middleware writes the store to localStorage. State survives page reloads. You can also use sessionStorage or a custom storage.
import { create } from "zustand";
import { persist } from "zustand/middleware";
export const useTheme = create(
persist<{ theme: "light" | "dark"; toggle: () => void }>(
(set) => ({
theme: "light",
toggle: () =>
set((s) => ({ theme: s.theme === "light" ? "dark" : "light" })),
}),
{ name: "theme" }
)
);Key Concepts
What you'll meet over and over:
- create — building a store
- set / get — updating and reading state
- selectors — subscribing to slices
- persist — saving state
- subscriber — listening outside React
- middleware — devtools, persist, immer, …
Zustand vs Redux
The high-level differences:
- Zustand: minimal boilerplate, no provider, fast setup
- Redux: actions/reducers/dispatch, stricter, more setup
- Zustand keeps growing in modern projects
- Redux Toolkit reduces classic Redux boilerplate but is still heavier
Use Cases
Where Zustand particularly shines:
- Global UI state (theme, modal, sidebar)
- Shared client state
- Small-to-medium apps
- React Native apps
- Form draft / auto-save state
Summary
Zustand is simple, fast and performant. It minimizes boilerplate, offers a great DX and is a fantastic default for modern React apps. The most common modern combo is React Query for server state and Zustand for client state.
Frequently Asked Questions
Common questions about this topic.
Zustand or Redux Toolkit?
For new projects, Zustand is usually enough and brings far less boilerplate. Very large teams or those wanting strict action history and devtools-driven workflows might still prefer Redux Toolkit.
Can I use it for server state?
You can, but it's not recommended. Libraries dedicated to server state — React Query / TanStack Query — handle cache, deduplication and refetch much better. Use Zustand for client state.
Will TypeScript be hard?
No — Zustand's TS support is solid. The create<TState>() generic gives you typed state and actions automatically. You only need careful types when composing middleware.
Can I have multiple stores?
Yes, that's the recommended pattern. Create a store per domain (auth, cart, ui) instead of a single mega-store — easier to test and reason about.
Is there DevTools support?
Yes — the devtools middleware integrates with Redux DevTools, letting you inspect state history and actions during development.
Related Posts
Other infographics on connected topics.
- ReactFrontend
What is React Query? Mutation Guide with Axios and TypeScript
Use React Query (TanStack Query) with an Axios baseURL, login mutation, create user and update user hooks in TypeScript.
- ReactFrontend
What is Next.js? The React Framework for Production
Build full-stack React apps with Next.js (App Router). Hands-on guide to SSR, SSG, ISR, API routes, Server Components and performance.
- ReactFrontend
What is React Router v7? Declarative Routing Made Simple
Use React Router v7 for declarative routing in React apps. Walkthrough of BrowserRouter, Routes, Link, useNavigate, nested routes and protected routes.
Discover more developer infographics
Visit the homepage so you don't miss new content.
See all infographics