React Context API vs Redux: When to Use Which
Context API: What It Does Well
Context is React's built-in solution for sharing state without prop drilling. It's a broadcast channel — set a value at the top, consume it anywhere below.
// 1. Create the context
const ThemeContext = createContext('light')
// 2. Provide at the top function App() { const [theme, setTheme] = useState('light') const value = useMemo(() => ({ theme, setTheme }), [theme]) return ( <ThemeContext.Provider value={value}> <Layout /> </ThemeContext.Provider> ) }
// 3. Consume anywhere below — no prop drilling function Button() { const { theme } = useContext(ThemeContext) return <button className={theme}>Click</button> }
Context is ideal for: current user, theme, locale, feature flags — values that change infrequently and are needed across many components.
Context's Limitations
The re-render problem: Every component that calls useContext(MyContext) re-renders when the context value changes — not just the parts of the value they actually use.
// ❌ One big context — a cart change re-renders Avatar and Settings
const AppContext = createContext({ user, cart, theme, setCart, setTheme })
// ✅ Split by domain and update frequency const UserContext = createContext({ user }) // rarely changes const CartContext = createContext({ cart, setCart }) // changes often const ThemeContext = createContext({ theme }) // changes rarely
No middleware: Context has no built-in support for async actions, logging, or side effects. You handle these manually.
No DevTools: No time-travel debugging or action history.
Verdict on Context: Great for 1-3 pieces of global state in small-to-medium apps. Starts hurting when you have many unrelated values that change at different frequencies.
Redux Toolkit: Structured State for Large Apps
Modern Redux uses Redux Toolkit (RTK) — the official, opinionated setup. Gone are the days of hand-written action types and reducers.
import { createSlice, configureStore } from '@reduxjs/toolkit'
const cartSlice = createSlice({ name: 'cart', initialState: { items: [] }, reducers: { addItem: (state, action) => { state.items.push(action.payload) // Immer makes mutations safe }, removeItem: (state, action) => { state.items = state.items.filter(i => i.id !== action.payload) }, }, })
export const { addItem, removeItem } = cartSlice.actions const store = configureStore({ reducer: { cart: cartSlice.reducer } })
// In components:
function Cart() {
const items = useSelector(state => state.cart.items)
const dispatch = useDispatch()
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.name}
<button onClick={() => dispatch(removeItem(item.id))}>Remove</button>
</li>
))}
</ul>
)
}
What Redux gives you that Context doesn't:
- Redux DevTools with time-travel debugging and action replays
- Fine-grained subscriptions via selectors — component only re-renders when its slice changes
- Middleware for async (Redux Thunk), logging, and analytics
- Predictable, normalized state structure
- Works outside React (useful for Web Workers, SSR hydration)
Modern Alternatives
Zustand — minimal API, no boilerplate, subscriptions out of the box:
const useStore = create(set => ({ count: 0, inc: () => set(s => ({ count: s.count + 1 })), }))
// Component only re-renders when count changes const count = useStore(s => s.count)
Jotai / Recoil — atomic state model. Components subscribe to individual atoms, not a global store. Each atom re-renders only its subscribers.
Decision Matrix
| Situation | Use | |---|---| | 1-3 pieces of global state (theme, user) | Context | | Complex state with many slices | Redux Toolkit or Zustand | | Need DevTools, time-travel debugging | Redux Toolkit | | Small/medium app, minimal boilerplate | Zustand | | Atomic state with isolated subscriptions | Jotai | | Async server data (API responses) | React Query or SWR |
The Interview Answer
"Context is the right choice for simple global state — current user, theme — in small to medium apps. Redux Toolkit is better for large apps with many state slices, teams needing DevTools, or complex async flows. Zustand is a lightweight middle ground with Redux-like subscriptions but without the boilerplate. For server data, I'd use React Query rather than putting API responses into either."
Practice at [JSPrep Pro](/auth).