Basic Docs
reactarchitecture

State Management

Compare Redux Toolkit, Zustand, and Context API to pick the right state solution for your app.

When Do You Need State Management?

Not every app needs a state management library. Most state starts local and only needs to be elevated when shared. Follow this progression before reaching for a library.

1
Local state
Start with useState and useReducer inside the component. Works well for isolated UI state like toggles, form inputs, or modal visibility.
2
Lifting state
Move state up to the closest common ancestor when two sibling components need to share it. No library needed.
3
Context API
Use for slow-changing shared values like theme, locale, or current user. Context is not optimized for high-frequency updates.
4
External store
Reach for Redux Toolkit or Zustand when state is complex, frequently updated, shared across many unrelated components, or needs time-travel debugging.
The golden rule
Keep state as close to where it is used as possible. Global state should be the last resort, not the default starting point.

Context API

Built into React — no extra dependency. Ideal for values that change infrequently and are consumed by many components at different depths. Wrapping the tree in a Provider is all the setup required.

ThemeContext with Provider and consumer
// ThemeContext.tsx
const ThemeContext = createContext<"light" | "dark">("light");

export function ThemeProvider({ children }: { children: ReactNode }) {
  const [theme, setTheme] = useState<"light" | "dark">("light");
  return (
    <ThemeContext.Provider value={theme}>
      {children}
    </ThemeContext.Provider>
  );
}

// Consuming the context
export function Header() {
  const theme = useContext(ThemeContext);
  return <header data-theme={theme}>...</header>;
}
!Context is not a performance tool
Every component that consumes a context re-renders when the context value changes. For high-frequency state (keystroke counters, live data), use Zustand or Redux instead.

Redux Toolkit

Redux Toolkit (RTK) is the official, opinionated way to write Redux. It eliminates the boilerplate of vanilla Redux with createSlice, createAsyncThunk, and Immer-powered mutable reducers.

Action
Reducer
Store
UI
Redux Data Flow
1
Action
Describes what happened in the app
2
Dispatch
Sends the action to the store for processing
3
Reducer
Pure function computing the next state
4
Store
Holds the entire application state tree
5
UI
Components re-render with the new state
6
New Action
User interaction triggers the next action
RTK slice and component
// counterSlice.ts
import { createSlice } from "@reduxjs/toolkit";

const counterSlice = createSlice({
  name: "counter",
  initialState: { value: 0 },
  reducers: {
    increment: (state) => { state.value += 1; },
    decrement: (state) => { state.value -= 1; },
  },
});

export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;

// Component
function Counter() {
  const count = useAppSelector((s) => s.counter.value);
  const dispatch = useAppDispatch();
  return <button onClick={() => dispatch(increment())}>{count}</button>;
}
iWhen RTK shines
Choose Redux Toolkit for large teams, complex async flows with RTK Query, or apps that benefit from Redux DevTools time-travel debugging.

Zustand

Zustand is a minimal state library — a single create() call returns a hook. No Provider, no boilerplate. The store is just a function that holds state and actions together.

Zustand store and component
// store.ts
import { create } from "zustand";

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

export const useCounterStore = create<CounterStore>((set) => ({
  count: 0,
  increment: () => set((s) => ({ count: s.count + 1 })),
  decrement: () => set((s) => ({ count: s.count - 1 })),
}));

// Component — no Provider needed
function Counter() {
  const { count, increment } = useCounterStore();
  return <button onClick={increment}>{count}</button>;
}
ZustandRedux Toolkit
~1 KB bundle~11 KB bundle
No Provider requiredRequires <Provider> at root
Minimal boilerplateSlice + store + hooks setup
No DevTools by defaultFirst-class DevTools support
Great for small–medium appsGreat for large, team-scale apps

Choosing the Right Tool

There is no universally correct answer — pick based on team size, update frequency, and how much tooling overhead you can justify.

Bundle Size Comparison
Context API0 KB
Zustand1 KB
Jotai2 KB
Redux Toolkit11 KB
CriteriaBest Choice
Infrequent global values (theme, locale)Context API
Simple shared state, small teamZustand
Complex async, large team, DevToolsRedux Toolkit
Server state (data fetching, caching)TanStack Query / RTK Query
Form stateReact Hook Form / Formik
Mix and match
Using Context for auth + Zustand for UI state + TanStack Query for server data is a perfectly valid and common architecture.
Built: 4/8/2026, 12:01:11 PM