webroad.online
  1. 1Web
  2. 2HTML
  3. 3CSS
  4. 4JavaScript
  5. 5TypeScript
  6. 6Git
  7. 7Tooling
  8. 8React
  9. 9State management
  10. 10Next.js
  11. 11Forms
  12. 12Data and backend
  13. 13SEO
  14. 14Tailwind CSS
  15. 15Animations
  16. 16Testing
  17. 17Architecture
State management · Lesson 3 of 4

Zustand

A minimalist global store: create, set with merging, selectors, persist.

Updated

What Zustand is

Zustand is a small (~1 KB) library for global state: a store created with a function, used like a hook. No provider, no string action types, no reducers — just state and the functions that change it.

import { create } from 'zustand'

type CartState = {
  items: number[]
  add: (id: number) => void
  clear: () => void
}

export const useCart = create<CartState>()(set => ({
  items: [],
  add: id => set(state => ({ items: [...state.items, id] })),
  clear: () => set({ items: [] }),
}))
'use client'
const count = useCart(state => state.items.length)   // a selector
const add = useCart(state => state.add)
<button onClick={() => add(product.id)}>Add</button>

How it works

  • create returns a hook. The store lives outside React; components subscribe to it.
  • set merges the new object into the existing state, at the first level — you don't have to copy everything.
  • A component re-renders only when the value returned by its selector changes.

Selectors — the most important detail

Code Re-renders when
useCart() (no selector) anything in the store changes ✗
useCart(s => s.items.length) only when the count changes ✓
useCart(s => ({ a: s.a, b: s.b })) always — a new object on every render ✗
useCart(useShallow(s => ({ a: s.a, b: s.b }))) when a or b changes ✓

The rule: select as little as possible; for several values, several calls or useShallow.

Useful middleware

import { persist, devtools } from 'zustand/middleware'

export const useSettings = create<Settings>()(
  persist(
    set => ({ theme: 'light', setTheme: theme => set({ theme }) }),
    { name: 'settings' },                 // the localStorage key
  ),
)
Middleware What it does
persist saves the store to localStorage / sessionStorage
devtools connects to Redux DevTools
immer "mutable" syntax, like in RTK

Zustand vs Redux Toolkit

Zustand Redux Toolkit
Code for a simple store ~10 lines a slice + a store + a provider + typed hooks
Provider not needed yes
Enforced structure little — you decide a lot — slices, actions
Server state no (use TanStack Query) RTK Query included
A good fit for most apps big apps, teams that want strict rules

Zustand with Next.js

A module-level global store is shared on the server between requests. The rules:

  • Use the store only in Client Components.
  • Don't put user-specific data in it during server rendering; for that, create the store per request (createStore + Context) — the official guide describes the pattern.
  • With persist, the value from localStorage appears only after hydration.

Where it lives in FSD

A store tied to an entity → entities/cart/model/cart-store.ts. A global UI store → shared/model/ or the slice that owns it.

Summary

  • Zustand = create → a hook; set merges; no provider.
  • Small selectors; useShallow for several values.
  • persist for localStorage; in Next only on the client, watch out for per-user data.

Official sources

Exercises

Was this page helpful?

One tap — no account needed.