In short
Zustand is a tiny state library that skips the ceremony. No provider, no actions file, just a store you use like a hook. Plenty of teams pick it for new projects.
A minimal, hook-based state management library — far less boilerplate than Redux, using a simple `create()` store accessed directly as a hook.

Think of it like this
Your gully's kirana store
No trolleys, no billing queue, no loyalty card. You walk up, ask for Maggi and get it. A supermarket (Redux) has more systems, which helps at a big scale. Zustand is the kirana: small, quick and enough for most daily needs.
Watch it happen
Only the right components update
add("Pen")
1/4ProductList calls add("Pen") from the store. No provider, no dispatch.
The big ideas
What you need to know
create() makes a hook
create returns a hook like useCart. Any component can call it, with no provider wrapping the app.
Select what you need
useCart((s) => s.items) re-renders only when items changes, not on every store update.
set merges for you
set({ open: true }) merges into the top level of the state, so you don't spread the rest yourself.
Middleware when needed
Add persist for localStorage or devtools for Redux DevTools support, both from zustand/middleware.
See it in code
Walk through the code
1import { create } from "zustand";2 3type CartState = {4 items: string[];5 add: (item: string) => void;6 clear: () => void;7};8 9export const useCart = create<CartState>()((set) => ({10 items: [],11 add: (item) => set((s) => ({ items: [...s.items, item] })),12 clear: () => set({ items: [] }),13}));14 15// const count = useCart((s) => s.items.length);16// const add = useCart((s) => s.add);1/4The state type lists the data and the functions that change it.
The create<CartState>()(...) double call is the TypeScript form Zustand's docs use. Each component selects only what it needs, so a header showing the count ignores unrelated changes.
Save yourself the pain
Common mistakes
Oops
Calling useCart() with no selector everywhere.
Do this instead
That re-renders on every store change. Pass a selector, like useCart((s) => s.items).
Oops
Selecting a new object, like useCart((s) => ({ a: s.a, b: s.b })).
Do this instead
In Zustand v5 that can cause an endless re-render loop. Wrap the selector in useShallow, or select each value separately.
Your mission
Replace Context with Zustand
Quick check
Header uses useCart((s) => s.items.length). Another component changes an unrelated filter value in the same store. Does Header re-render?
Go deeper
- 1Convert a Context-based global state into a Zustand store.
- 2Notice how much less boilerplate it takes compared to Redux Toolkit.
- 3Learn Zustand's selector pattern to avoid unnecessary re-renders.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.