Stop 09 · State ManagementTopics · 4/7

Zustand

Part of the Frontend Developer Roadmap

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.

The jaadu mascot is handed a packet of noodles across the counter of a small grocery shop.

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

ProductList
useCart store
Header
Filters

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

ts
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

~25 min

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

  1. 1Convert a Context-based global state into a Zustand store.
  2. 2Notice how much less boilerplate it takes compared to Redux Toolkit.
  3. 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.

Download
InsideEdge