Zustand State Management
Introduction
Zustand is a small global state library for React—stores hold shared data (auth, todos, UI flags) outside the component tree. It is simpler than Redux for many SPAs and maps closely to Vue Pinia. This chapter covers create, selectors, async actions, and when to use Zustand vs Context vs local state.
Prerequisites
Install
bash
npm install zustandBasic Store
src/stores/counterStore.ts:
tsx
CounterDisplay.tsx:
tsx
Code explanation:
createreturns a hookuseCounterStore- Selectors
(s) => s.countsubscribe only to that slice—fewer re-renders setmerges partial state; functional form likeuseState
Todo Store (Preview)
src/stores/todoStore.ts:
tsx
Full UI in Todo project—sister to Vue Pinia Todo.
Async Actions
tsx
Persist Middleware (Concept)
bash
npm install zustandtsx
import { create } from "zustand";
import { persist } from "zustand/middleware";
export const useTodoStore = create(
persist<TodoState>(
(set, get) => ({
/* ... */
}),
{ name: "hello-react-todos" }
)
);Warning
JWT in localStorage
Convenient for tutorials; production may prefer httpOnly cookies—see FastAPI JWT.
Zustand vs Context vs useState
| Need | Tool |
|---|---|
| One component | useState |
| Theme, rare updates | Context |
| Todos, auth, cart | Zustand |
| Large enterprise with middleware | Redux Toolkit (external docs) |
Outside React
tsx
const token = useAuthStore.getState().token;
useAuthStore.getState().logout();Use sparingly—in router setup before React tree mounts.
FAQ
Subscribe to whole store?
tsx
const store = useCounterStore(); // re-renders on any change — avoidPrefer selectors.
Immer for nested updates?
tsx
npm install immer
// zustand/middleware immer — optional for deep treesMultiple stores?
Yes—useTodoStore, useAuthStore separate files.
Pinia migration mindset?
Pinia defineStore ≈ Zustand create; storeToRefs ≈ fine-grained selectors.