Zustand for Client State

Create small persisted stores with Zustand, use selectors, and keep stores focused.

Zustand is a minimal state library. A store is a hook; components subscribe to just the slice they use.

Core concepts

  1. Create a store: Call create with a function that returns state and actions. Components call the hook to read state or actions. — Zustand docs
  2. Selectors: Select only what a component needs, for example useCart((s) => s.ids.length), so unrelated changes do not re-render it. — Zustand docs
  3. Persist middleware: Persist selected state to AsyncStorage or MMKV with persist and createJSONStorage, and use a version plus migrate when the shape changes. — Zustand persist
  4. Keep stores small: One store per concern (auth session, filters, cart). Avoid a single store for the whole app. — Zustand docs

Concept videos

YouTube channels and searches for these topics. On a large screen they open in the reading panel next to the roadmap.

Explanation

Migrations matter

If you rename a persisted field, old installs load the old shape and crash. Use the persist version and a migrate function.

Secrets do not belong here

Persisted Zustand state is plain storage. Store tokens in SecureStore, not in a persisted store.

What Is It?

Zustand is a small library for shared client-side state using hooks.

Why Should I Care?

It is simple enough for agents to use correctly and avoids the boilerplate of larger libraries.

Real World Analogy

A Zustand store is a shared whiteboard with named sections; each reader only looks at their section.

Visual Mental Model

text
create(store) -> useStore(selector) -> component re-renders only on that slice

Smallest Possible Example

ts
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';

export const useSettings = create(
  persist<{ dark: boolean; toggle: () => void }>(
    (set) => ({ dark: false, toggle: () => set((s) => ({ dark: !s.dark })) }),
    { name: 'settings', storage: createJSONStorage(() => AsyncStorage), version: 1 }
  )
);

Common Mistakes

❌ Selecting the whole store in every component.

That re-renders on any change; select slices.

❌ Persisting tokens in a normal store.

Use SecureStore for secrets.

Build This Mini Project

Create a persisted settings store with a version and a migrate function, and a screen that toggles theme.

Prompts for Cursor, Claude Code & Codex

Paste these into Cursor, Claude Code, Codex, or any coding agent. Replace the bracketed parts with your app details, and keep the agent in plan mode first so you review the approach before it edits files.

Add persisted store

text
Create lib/stores/settings.ts with Zustand and persist (AsyncStorage). Include version 1 and a migrate stub, and selectors in the settings screen. Do not store secrets.

Rules, databases & blogs to use

Resources

Threads in the same domain as this chapter — go deeper on iHateReading without leaving the roadmap.

How to build an AI chat app with Vercel AI SDK and Firebase cover

How to build an AI chat app with Vercel AI SDK and Firebase

Chat UI, streaming and backend wiring for an AI feature.

Agent harnessing: the part of building AI agents nobody talks about cover

Agent harnessing: the part of building AI agents nobody talks about

Loop, tools, memory, sandbox, and tracing live in your code — not the model weights.