TrueSpec

Zustand State Management

Store setup, slices, persist middleware, devtools, and TypeScript

What You’ll Build

After following this guide, you will have a working implementation of zustand state management in your project. Zustand is the simplest state management library for React. No providers, no context, no boilerplate — just a hook. Create a store in 5 lines, use it anywhere. Supports middleware for persistence, devtools integration, and computed values.

Use Cases & Problems Solved

  • Implement interactive UI features that users expect from modern apps
  • Follow established patterns that scale and remain maintainable
  • Reduce boilerplate and avoid common frontend pitfalls

Prerequisites

  • React 18+

Step-by-Step Implementation

Install Zustand

The following snippet shows how to install zustand. Copy this into your project and adjust the values for your environment.

npm install zustand

Create a store

The following snippet shows how to create a store. Copy this into your project and adjust the values for your environment.

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

interface CartStore {
  items: { id: string; name: string; qty: number }[];
  addItem: (item: { id: string; name: string }) => void;
  removeItem: (id: string) => void;
  clearCart: () => void;
  totalItems: () => number;
}

export const useCart = create<CartStore>()(
  devtools(
    persist(
      (set, get) => ({
        items: [],
        addItem: (item) => set((state) => {
          const existing = state.items.find(i => i.id === item.id);
          if (existing) {
            return { items: state.items.map(i =>
              i.id === item.id ? { ...i, qty: i.qty + 1 } : i
            )};
          }
          return { items: [...state.items, { ...item, qty: 1 }] };
        }),
        removeItem: (id) => set((state) => ({
          items: state.items.filter(i => i.id !== id),
        })),
        clearCart: () => set({ items: [] }),
        totalItems: () => get().items.reduce((sum, i) => sum + i.qty, 0),
      }),
      { name: 'cart-storage' } // persists to localStorage
    )
  )
);

// Use anywhere — no Provider needed!
function CartButton() {
  const totalItems = useCart(state => state.totalItems());
  return <button>Cart ({totalItems})</button>;
}

⚠️ Don’t Do This

❌ Subscribing to the entire store (causes unnecessary re-renders)

// Re-renders this component when ANY store value changes!
const store = useCart(); // Subscribes to everything
return <p>Items: {store.items.length}</p>;

✅ Select only the values you need

// Only re-renders when items.length changes
const itemCount = useCart(state => state.items.length);
return <p>Items: {itemCount}</p>;

Testing

Verify your implementation with these tests:

// __tests__/zustand-state-management.test.ts
import { describe, it, expect } from 'vitest';

describe('Zustand State Management', () => {
  it('should initialize without errors', () => {
    // Test that the setup completes successfully
    expect(() => setup()).not.toThrow();
  });

  it('should handle the primary use case', async () => {
    const result = await execute();
    expect(result).toBeDefined();
    expect(result.success).toBe(true);
  });

  it('should handle edge cases', async () => {
    // Test with empty/null input
    const result = await execute(null);
    expect(result.error).toBeDefined();
  });
});

Verification

# Add items to cart, refresh the page
# Items should persist (localStorage)
# Open Redux DevTools → see Zustand store state
# Verify selective re-renders with React DevTools Profiler

Related Specs

Intermediate

Infinite Scroll with Virtualization

TanStack Virtual + Intersection Observer for smooth infinite lists

Frontend Patterns
Beginner

React Form Validation (React Hook Form + Zod)

Type-safe forms, custom validators, error display, and submission handling

Frontend Patterns
Beginner

Toast Notification System

Custom toast component, queue management, animations, and accessibility

Frontend Patterns