← Back to all products

State Management Patterns

$29

Zustand, Jotai, and React Query patterns for complex state: optimistic updates, caching, and real-time sync.

📁 25 files
JSONMarkdownTypeScriptReact

📄 Product Preview

Try the interactive reader and demo tools below, or get the full product with all content unlocked.

📖 Interactive Reader (Free Preview) ⚙ Try Demo Tools 📦 Download Free Sample

📁 File Structure 25 files

state-management-patterns/ ├── LICENSE ├── README.md ├── examples/ │ ├── dashboard-jotai.tsx │ ├── data-table-react-query.tsx │ └── todo-app-zustand.tsx ├── free-sample.zip ├── guide/ │ └── decision-guide.md ├── guides/ │ └── decision-guide.md ├── index.html ├── src/ │ ├── jotai/ │ │ ├── async-atoms.ts │ │ ├── atom-families.ts │ │ └── atoms.ts │ ├── react-query/ │ │ ├── cache-invalidation.ts │ │ ├── infinite-queries.ts │ │ ├── mutations.ts │ │ └── queries.ts │ ├── realtime/ │ │ ├── optimistic-updates.ts │ │ └── websocket-sync.ts │ └── zustand/ │ ├── middleware.ts │ ├── persistence.ts │ └── store-slices.ts ├── tests/ │ ├── jotai.test.ts │ ├── react-query.test.ts │ └── zustand.test.ts └── tsconfig.json

📖 Documentation Preview README excerpt

State Management Patterns

Battle-tested patterns for managing complex client state in React applications using Zustand, Jotai, and React Query. This collection covers the patterns that emerge when you move beyond simple useState — optimistic updates, cache synchronization, real-time data, and the hard decisions about where state actually belongs.

What's Inside

Zustand Patterns (`src/zustand/`)

  • Store Slices — Split large stores into composable slices that share a single store instance. Includes typed slice creators and cross-slice communication.
  • Middleware Stack — Logging, devtools integration, performance tracking, and immer middleware composed together.
  • Persistence Layer — Storage adapters (localStorage, sessionStorage, IndexedDB) with migration support for evolving store shapes.

Jotai Patterns (`src/jotai/`)

  • Atoms & Derived Atoms — Primitive atoms, computed atoms with selectors, and write-only atoms for complex updates.
  • Async Atoms — Data fetching atoms with loading/error states, retry logic, and Suspense integration.
  • Atom Families — Parameterized atoms for managing collections (e.g., per-item expanded state, per-tab filters).

React Query Patterns (`src/react-query/`)

  • Query Patterns — Dependent queries, parallel queries, conditional fetching, and placeholder/initial data strategies.
  • Mutations & Optimistic Updates — Mutation patterns with rollback, optimistic list updates, and toast notifications on failure.
  • Infinite Queries — Cursor-based and offset-based infinite scroll with bi-directional support.
  • Cache Invalidation — Granular invalidation, cache seeding from mutations, and stale-while-revalidate strategies.

Real-Time Sync (`src/realtime/`)

  • WebSocket Integration — Reconnecting WebSocket manager that syncs server events into Zustand and React Query caches.
  • Optimistic Updates — Shared patterns for optimistic UI across all three libraries, with server reconciliation.

Decision Guide (`guides/decision-guide.md`)

When should you reach for Zustand vs. Jotai vs. React Query? A decision tree based on the _shape_ of your state problem, not library popularity.

Working Examples (`examples/`)

  • Todo app with Zustand (local-first with sync)
  • Dashboard with Jotai (fine-grained reactivity)
  • Data table with React Query (server state with filtering)

Quick Start


# Install the libraries you want to use (pick one or combine):
npm install zustand immer
npm install jotai
npm install @tanstack/react-query

# Copy the patterns you need into your project
cp -r src/zustand/ your-project/src/state/

Each pattern file is self-contained. Import what you need, ignore the rest.

File Structure


state-management-patterns/
├── README.md
├── LICENSE
├── tsconfig.json
├── src/
│   ├── zustand/
│   │   ├── store-slices.ts           # Composable store slices
│   │   ├── middleware.ts             # Custom middleware stack
│   │   └── persistence.ts           # Storage adapters & migrations

*... continues with setup instructions, usage examples, and more.*

📄 Code Sample .ts preview

src/jotai/async-atoms.ts // ============================================================================ // Jotai Async Atoms — Data Fetching with Loading & Error States // ============================================================================ // // Async atoms bridge the gap between server state and client state. They're // Jotai's answer to the question: "Should I use React Query or can I handle // data fetching with atoms?" // // The answer: use async atoms for data that's closely tied to other atom // state (e.g., fetch products based on `selectedCategoryAtom`). Use React // Query when you need built-in caching, background refetching, or pagination. // // Async atoms integrate with React Suspense, so your loading UI is handled // at the boundary level rather than with `if (isLoading)` checks. // // ============================================================================ import { atom } from "jotai"; import { atomWithRefresh, loadable } from "jotai/utils"; import { selectedCategoryAtom, searchQueryAtom, sortOptionAtom } from "./atoms"; import type { Product, SortOption } from "./atoms"; // --------------------------------------------------------------------------- // Mock API — replace with your real API client // --------------------------------------------------------------------------- const API_BASE = "https://api.example.com/v1"; interface PaginatedResponse<T> { data: T[]; total: number; page: number; pageSize: number; hasMore: boolean; } /** * Simulate a network request with realistic delay and error rates. * In your codebase, this would be `fetch()` or an Axios call. */ async function mockFetch<T>( endpoint: string, params: Record<string, string> = {}, options: { delay?: number; errorRate?: number } = {} ): Promise<T> { const { delay = 300, errorRate = 0 } = options; // Simulate network latency await new Promise((resolve) => setTimeout(resolve, delay)); # ... 310 more lines ...
Buy Now — $29 Back to Products