DEV Community

Alex Spinov
Alex Spinov

Posted on

Zustand Has a Free State Management Library — Here's How to Use It

Redux needs 4 files to add one piece of state. Context API re-renders everything. Zustand gives you global state in 3 lines — no providers, no boilerplate, no re-render problems.

What Is Zustand?

Zustand is a small, fast state management library for React. It's 1KB, requires no providers, and only re-renders components that use changed state.

Quick Start

npm install zustand
Enter fullscreen mode Exit fullscreen mode
import { create } from 'zustand';

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

function Counter() {
  const count = useStore((state) => state.count);
  const increment = useStore((state) => state.increment);

  return <button onClick={increment}>{count}</button>;
}
Enter fullscreen mode Exit fullscreen mode

That's it. No <Provider>, no reducer, no action types, no dispatch.

Why Zustand Wins

Feature Zustand Redux Toolkit Context API
Boilerplate ~5 lines ~30 lines ~15 lines
Bundle size 1KB 11KB 0 (built-in)
Provider needed No Yes Yes
Re-render control Selector-based Selector-based Re-renders all
Async actions Just use async/await Thunks/Sagas Manual
DevTools Yes (middleware) Yes React DevTools
Learning curve 5 minutes 1-2 hours 30 minutes

Real-World Patterns

Async Data Fetching

const useStore = create((set) => ({
  users: [],
  loading: false,
  error: null,

  fetchUsers: async () => {
    set({ loading: true, error: null });
    try {
      const res = await fetch('/api/users');
      const users = await res.json();
      set({ users, loading: false });
    } catch (error) {
      set({ error: error.message, loading: false });
    }
  },
}));
Enter fullscreen mode Exit fullscreen mode

Persistence (localStorage)

import { persist } from 'zustand/middleware';

const useStore = create(
  persist(
    (set) => ({
      theme: 'light',
      toggleTheme: () => set((s) => ({ 
        theme: s.theme === 'light' ? 'dark' : 'light' 
      })),
    }),
    { name: 'app-settings' } // localStorage key
  )
);
Enter fullscreen mode Exit fullscreen mode

Slices Pattern (Large Apps)

const createAuthSlice = (set) => ({
  user: null,
  login: async (credentials) => {
    const user = await authApi.login(credentials);
    set({ user });
  },
  logout: () => set({ user: null }),
});

const createCartSlice = (set) => ({
  items: [],
  addItem: (item) => set((s) => ({ items: [...s.items, item] })),
  total: () => get().items.reduce((sum, i) => sum + i.price, 0),
});

const useStore = create((...args) => ({
  ...createAuthSlice(...args),
  ...createCartSlice(...args),
}));
Enter fullscreen mode Exit fullscreen mode

Key Features

  • No providers — works outside React too
  • Selector-based re-renders — only what changes
  • Middleware — persist, devtools, immer, subscribeWithSelector
  • TypeScript — full type inference
  • Server-compatible — works with SSR
  • Framework agnostic — vanilla version available

Get Started


Need real-time data in your React app? My Apify scrapers extract fresh data from any website. Custom solutions: spinov001@gmail.com

Top comments (0)