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

Published: (March 29, 2026 at 02:10 PM EDT)
4 min read
Source: Dev.to

Source: Dev.to

Redux often requires actions, reducers, selectors, middleware, and dozens of lines of boilerplate even for a simple counter.
Zustand delivers the same capabilities in just a handful of lines—no actions, reducers, dispatchers, or Provider components required.

Minimal API

  • Create a store in a few lines and use it anywhere.
  • No boilerplate—no actions, reducers, or context providers.
  • No <Provider> wrapper—just import the hook.
  • TypeScript‑first—fully typed out of the box.
  • Built‑in middleware support (persist, devtools, immer, subscriptions).
  • ~1 KB gzipped (vs. ~30 KB for Redux Toolkit).

Installation

npm install zustand

Counter Example

import { create } from 'zustand';

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

const useCounter = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}));

// Any component can import and use the store directly
function Counter() {
  const count = useCounter((state) => state.count);
  const increment = useCounter((state) => state.increment);

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

No <Provider> wrapper, no connect(), and no useSelector/useDispatch needed.

Auth Store with Persistence

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

interface User {
  id: string;
  name: string;
  email: string;
}

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

const useAuth = create()(
  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
  )
);

Todo Store (Async Actions)

import { create } from 'zustand';

interface Todo {
  id: string;
  title: string;
  done: boolean;
}

interface TodoStore {
  todos: Todo[];
  loading: boolean;
  fetchTodos: () => Promise;
  addTodo: (title: string) => Promise;
  toggleTodo: (id: string) => void;
}

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

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

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

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

Async logic is written with plain async/await; no createAsyncThunk or extra middleware required.

Middleware Stack Example

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

interface Store {
  items: string[];
  addItem: (item: string) => void;
}

const useStore = create()(
  devtools(
    persist(
      immer((set) => ({
        items: [],
        addItem: (item) =>
          set((state) => {
            state.items.push(item); // Immer makes this mutable update safe
          }),
      })),
      { name: 'my-store' }
    )
  )
);

Combines Redux DevTools support, localStorage persistence, and Immer‑based mutable updates.

Feature Comparison

FeatureZustandRedux ToolkitJotaiRecoil
Bundle size~1 KB~30 KB~3 KB~20 KB
BoilerplateMinimalMediumMinimalMedium
Provider neededNoYesYesYes
Async handlingBuilt‑increateAsyncThunkBuilt‑inSelectors
DevToolsBuilt‑inPluginBuilt‑inPlugin
Learning curve~5 min~1 hour~15 min~30 min
TypeScript supportExcellentGoodExcellentGood

TL;DR

Zustand is a state‑management solution for developers who want zero ceremony: a tiny bundle, no Provider, no reducers, and a simple, type‑safe API. If Redux feels heavyweight for your project, Zustand offers a lightweight alternative without sacrificing power.

0 views
Back to Blog

Related posts

Read more »