DEV Community

Alex Spinov
Alex Spinov

Posted on

Zustand Has a Free State Manager That Replaces Redux in 10 Lines

Redux needs actions, reducers, selectors, middleware, and 200 lines of boilerplate for a counter. Zustand needs 10 lines. Same power, zero ceremony.

What Zustand Gives You for Free

  • Minimal API — create store in 5 lines, use it anywhere
  • No boilerplate — no actions, reducers, dispatchers, or context providers
  • No Provider wrapper — just import and use
  • TypeScript-first — fully typed without extra config
  • Middleware — persist, devtools, immer, subscriptions
  • 1KB gzipped — vs Redux Toolkit's 30KB+

Quick Start

npm install zustand
Enter fullscreen mode Exit fullscreen mode

Your First Store (10 Lines)

import { create } from 'zustand';

interface CounterStore {
  count: number;
  increment: () => void;
  decrement: () => void;
  reset: () => void;
}

const useCounter = create<CounterStore>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}));
Enter fullscreen mode Exit fullscreen mode

Using It (No Provider Needed)

// ANY component, ANY file — just import and use
function Counter() {
  const count = useCounter((state) => state.count);
  const increment = useCounter((state) => state.increment);

  return (
    <button onClick={increment}>
      Count: {count}
    </button>
  );
}

// No <Provider> wrapper in App.tsx!
// No connect() or useSelector/useDispatch
Enter fullscreen mode Exit fullscreen mode

Real-World Example: Auth Store

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

interface AuthStore {
  user: User | null;
  token: string | null;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
  isAuthenticated: () => boolean;
}

const useAuth = create<AuthStore>()(
  persist(
    (set, get) => ({
      user: null,
      token: null,

      login: async (email, password) => {
        const { user, token } = await api.login(email, password);
        set({ user, token });
      },

      logout: () => set({ user: null, token: null }),

      isAuthenticated: () => get().token !== null,
    }),
    { name: 'auth-storage' } // Persists to localStorage
  )
);
Enter fullscreen mode Exit fullscreen mode

Async Actions (No Thunks Needed)

const useTodos = create<TodoStore>((set, get) => ({
  todos: [],
  loading: false,

  fetchTodos: async () => {
    set({ loading: true });
    const todos = await api.getTodos();
    set({ todos, loading: false });
  },

  addTodo: async (title: string) => {
    const todo = await api.createTodo({ title });
    set((state) => ({ todos: [...state.todos, todo] }));
  },

  toggleTodo: (id: string) => {
    set((state) => ({
      todos: state.todos.map(t => 
        t.id === id ? { ...t, done: !t.done } : t
      )
    }));
  },
}));
Enter fullscreen mode Exit fullscreen mode

No createAsyncThunk. No middleware. Just async/await.

Middleware Stack

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

const useStore = create<Store>()(
  devtools(       // Redux DevTools support
    persist(      // localStorage persistence
      immer(      // Immer for mutable updates
        (set) => ({
          items: [],
          addItem: (item) => set((state) => {
            state.items.push(item); // Mutable! (Immer handles it)
          }),
        })
      ),
      { name: 'my-store' }
    )
  )
);
Enter fullscreen mode Exit fullscreen mode

Zustand vs Redux vs Jotai vs Recoil

Feature Zustand Redux Toolkit Jotai Recoil
Bundle size 1KB 30KB 3KB 20KB
Boilerplate Minimal Medium Minimal Medium
Provider Not needed Required Required Required
Async Built-in createAsyncThunk Built-in Selectors
DevTools Plugin Built-in Plugin Built-in
Learning curve 5 min 1 hour 15 min 30 min
TypeScript Excellent Good Excellent Good

The Verdict

Zustand is state management for people who hate state management boilerplate. 1KB, no providers, no reducers, just functions that update state. If Redux feels like too much ceremony, Zustand is the answer.


Need help building production web scrapers or data pipelines? I build custom solutions. Reach out: spinov001@gmail.com

Check out my awesome-web-scraping collection — 400+ tools for extracting web data.

Top comments (0)