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
Infinite Scroll with Virtualization
TanStack Virtual + Intersection Observer for smooth infinite lists
React Form Validation (React Hook Form + Zod)
Type-safe forms, custom validators, error display, and submission handling
Toast Notification System
Custom toast component, queue management, animations, and accessibility