Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →

State Management in React 2025: Redux vs Zustand vs Jotai

Redux vs Zustand vs Jotai compared for React 2025: when to use each, real code examples, and which state management solution fits your app.

A
AiTechWorlds Team
May 27, 2026 7 min read
📱

Get more content like this on Telegram!

Daily AI tips, notes & resources — free

Join Free →

State Management in React 2025: Redux vs Zustand vs Jotai

The most common question I get from developers moving from tutorials to real apps: "Do I need Redux?"

In 2019, the answer was usually yes. In 2025, the answer is almost always no — at least not at first.

React hooks changed state management dramatically. useState handles component-local state. useReducer handles complex state logic. useContext shares state across components. For most apps, that's enough.

But when it's not enough — when you have global state that dozens of components need, when prop drilling becomes painful, when you need cross-component state synchronization — that's when you need a library.

This guide compares the real options in 2025: Redux Toolkit, Zustand, and Jotai. Not theoretical comparison — concrete code showing when each fits.


First: When You Don't Need a Library

Before picking a state management library, consider whether you need one:

// Context + useReducer handles a lot
interface AppState {
  user: User | null;
  theme: 'light' | 'dark';
}

type Action = 
  | { type: 'SET_USER'; payload: User }
  | { type: 'LOGOUT' }
  | { type: 'TOGGLE_THEME' };

function reducer(state: AppState, action: Action): AppState {
  switch (action.type) {
    case 'SET_USER': return { ...state, user: action.payload };
    case 'LOGOUT': return { ...state, user: null };
    case 'TOGGLE_THEME': return { ...state, theme: state.theme === 'light' ? 'dark' : 'light' };
    default: return state;
  }
}

const AppContext = createContext<{ state: AppState; dispatch: Dispatch<Action> } | null>(null);

function AppProvider({ children }: { children: React.ReactNode }) {
  const [state, dispatch] = useReducer(reducer, { user: null, theme: 'light' });
  return <AppContext.Provider value={{ state, dispatch }}>{children}</AppContext.Provider>;
}

Context works great for state that changes infrequently (theme, auth, locale). It has one significant problem: every component that consumes the context re-renders when any part of the context changes. For frequently-changing state (filters, UI state, form data), this causes performance problems.

That's when you graduate to a library.


Option 1: Zustand — The Sweet Spot

Zustand is a tiny (1KB), fast state management library with a simple API. It's become the go-to Redux alternative for most React apps in 2025.

npm install zustand

Creating a Store

import { create } from 'zustand';

interface CartItem {
  id: number;
  name: string;
  price: number;
  quantity: number;
}

interface CartStore {
  items: CartItem[];
  addItem: (item: Omit<CartItem, 'quantity'>) => void;
  removeItem: (id: number) => void;
  updateQuantity: (id: number, quantity: number) => void;
  total: () => number;
  clearCart: () => void;
}

export const useCartStore = create<CartStore>((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, quantity: i.quantity + 1 } : i
        )
      };
    }
    return { items: [...state.items, { ...item, quantity: 1 }] };
  }),
  
  removeItem: (id) => set((state) => ({
    items: state.items.filter(i => i.id !== id)
  })),
  
  updateQuantity: (id, quantity) => set((state) => ({
    items: state.items.map(i => i.id === id ? { ...i, quantity } : i)
  })),
  
  total: () => get().items.reduce((sum, item) => sum + item.price * item.quantity, 0),
  
  clearCart: () => set({ items: [] }),
}));

Using the Store

// Any component, anywhere in the tree — no Provider needed
function CartIcon() {
  const items = useCartStore(state => state.items);  // Selective subscription
  const total = useCartStore(state => state.total);
  
  return (
    <div>
      <span>{items.length} items</span>
      <span>${total().toFixed(2)}</span>
    </div>
  );
}

function ProductCard({ product }) {
  const addItem = useCartStore(state => state.addItem);
  
  return (
    <div>
      <h3>{product.name}</h3>
      <button onClick={() => addItem(product)}>Add to Cart</button>
    </div>
  );
}

What I love about Zustand: no Provider wrapping, selective subscriptions prevent unnecessary re-renders, and the API is just a function.

Zustand with Persistence

import { persist } from 'zustand/middleware';

export const useCartStore = create<CartStore>()(
  persist(
    (set, get) => ({
      // ... store definition
    }),
    { name: 'cart-storage' }  // Key in localStorage
  )
);

Option 2: Jotai — Atomic State

Jotai takes a different approach: instead of one store, you have individual atoms that components subscribe to independently.

npm install jotai

Creating Atoms

import { atom } from 'jotai';

// Primitive atoms
export const userAtom = atom<User | null>(null);
export const themeAtom = atom<'light' | 'dark'>('light');

// Derived atom (computed from other atoms)
export const isAdminAtom = atom((get) => {
  const user = get(userAtom);
  return user?.role === 'admin';
});

// Async atom
export const userPostsAtom = atom(async (get) => {
  const user = get(userAtom);
  if (!user) return [];
  const response = await fetch(`/api/posts?userId=${user.id}`);
  return response.json();
});

Using Atoms

import { useAtom, useAtomValue, useSetAtom } from 'jotai';

function UserProfile() {
  const [user, setUser] = useAtom(userAtom);    // Read + write
  const isAdmin = useAtomValue(isAdminAtom);    // Read only
  
  return (
    <div>
      {user ? <p>Hello, {user.name}</p> : <p>Not logged in</p>}
      {isAdmin && <AdminPanel />}
    </div>
  );
}

function LoginButton() {
  const setUser = useSetAtom(userAtom);   // Write only — no re-render on value change
  
  return (
    <button onClick={() => setUser({ id: 1, name: 'Alice', role: 'user' })}>
      Login
    </button>
  );
}

Jotai's strength: components only re-render when the specific atoms they subscribe to change. This makes it extremely efficient for fine-grained updates.


Option 3: Redux Toolkit — When It's Still Worth It

Redux Toolkit (RTK) is the modern way to use Redux. The old boilerplate-heavy Redux is dead — RTK writes 90% of the code for you.

npm install @reduxjs/toolkit react-redux
import { createSlice, configureStore } from '@reduxjs/toolkit';

const notificationsSlice = createSlice({
  name: 'notifications',
  initialState: { items: [], unread: 0 },
  reducers: {
    addNotification: (state, action) => {
      state.items.unshift(action.payload);  // Immer makes mutations safe
      state.unread++;
    },
    markAllRead: (state) => {
      state.items.forEach(n => { n.read = true; });
      state.unread = 0;
    },
  },
});

const store = configureStore({
  reducer: { notifications: notificationsSlice.reducer }
});

Redux Toolkit makes sense when:

  • Your team already knows Redux
  • You need Redux DevTools time-travel debugging
  • Complex state machines with many interdependent slices
  • Large enterprise apps where strict patterns matter

Comparison at a Glance

ZustandJotaiRedux Toolkit
Bundle size~1KB~3KB~11KB
Learning curveLowLow-MediumMedium
BoilerplateVery littleVery littleSome
DevToolsBasicGoodExcellent
Provider needed✅ (optional)
Best forStore-based app stateFine-grained atomic stateLarge apps with complex state
SSR supportGoodExcellentGood

My Recommendation for 2025

Small to medium apps: Use useState + useContext. Add Zustand when you hit re-render performance issues or prop drilling pain.

Medium to large apps: Zustand for client state + React Query/TanStack Query for server state. This combination handles 95% of React apps.

Apps with complex derived state: Jotai is worth the mental model shift.

Existing Redux codebases: Migrate to Redux Toolkit if you haven't — don't rewrite to Zustand unless there's a good reason.

For the React fundamentals that underpin all of this, see our React tutorial for beginners. State management decisions depend on your framework choice too — our React vs Next.js vs Remix comparison covers how server rendering changes the picture. And for the React hooks that manage local state before you need a library, our React hooks tutorial covers them all.


Frequently Asked Questions

Do I need Redux in a React app in 2025?

Probably not. Context + hooks handles most cases. Zustand is a lighter alternative when you do need global state. Redux makes sense for large apps with existing Redux or complex state logic.

What is the difference between Zustand and Jotai?

Zustand: store-based, simple API, great for related state. Jotai: atomic, fine-grained subscriptions, great for derived/computed state.

What state goes global vs local?

Local: form inputs, open/closed UI state, component-specific data. Global: authenticated user, cart, preferences, data shared across many components.

What is React Query?

TanStack Query manages server state — fetching, caching, and syncing. Pair it with Zustand (client state) for a complete state management solution.

Share this article:

Frequently Asked Questions

Probably not. Redux was essential before React hooks — but useState, useContext, and useReducer handle most cases that used to require Redux. For client state that needs to be shared across many components, Zustand or Jotai are lighter and easier. Redux remains valid for very large apps with complex state logic, time-travel debugging needs, or existing Redux codebases.
A

AiTechWorlds Team

✓ Verified Writer

The AiTechWorlds team is passionate about AI, technology, and education. We create high-quality, research-backed content to help you learn, grow, and succeed in the modern digital world.

Related Articles

10K+ Members Growing Daily

Get Free AI Notes Daily

Join AiTechWorlds on Telegram and get daily AI tips, prompt engineering templates, coding resources, and exclusive content — 100% free!

📚 Free Study Notes🤖 AI Tips Daily⚡ Prompt Templates💻 Coding Resources
Join Free Channel

No spam. Leave anytime.

!