React Context vs Redux: Decision Guide for Global State in React 18
Deciding between React Context and Redux for global state? This guide outlines constraints, compares options in a table, explains trade‑offs, and shows a concrete TypeScript example that validates performance and bundle size.
18 Oct 2025, 17:01 UTC

Problem
When a React 18 application grows, multiple components often need access to shared data. Developers typically choose between the built‑in React Context API and the external Redux library. Both can satisfy the requirement, but they differ in performance, tooling, and team ergonomics. This article helps you decide which pattern fits your project’s constraints.
Decision Constraints
- Scope of shared data: Is the state small (theme, locale) or large (lists, derived calculations)?
- Update frequency: Does the value change often or only rarely?
- Team size & experience: Are developers familiar with Redux patterns, or would a lightweight solution be preferable?
- Tooling needs: Do you require time‑travel debugging, middleware, or side‑effect management?
- Bundle size & runtime cost: Is a minimal footprint critical?
Comparison Table
| Feature | React Context | Redux | Notes |
|---|---|---|---|
| Setup effort | Zero – native API | Requires redux + react‑redux (or @reduxjs/toolkit) |
Context is plug‑and‑play; Redux needs boilerplate. |
| State size | Best for small to medium objects | Handles large, nested state trees | Context re‑renders all consumers on any change. |
| Update frequency | Not ideal for frequent updates unless memoized or split | Optimized for high‑frequency updates via reducers | Use React.memo or useMemo with Context. |
| Debugging & dev‑tools | Limited – React DevTools shows provider values | Redux DevTools offers time‑travel, action logs, and state snapshots | Critical for complex state logic. |
| Middleware & side‑effects | No built‑in support | Redux‑Thunk, Saga, RTK Query, etc. | Choose if you need structured async flows. |
| Bundle size | Minimal – no external dependency | ~12 kB gzipped for Redux Toolkit; adds runtime overhead | Consider createSlice to keep it lean. |
| TypeScript support | Native types; manual typing of values | RTK offers strong typing via createSlice |
Both work well with TS. |
Trade‑offs
- Performance: Context forces a full re‑render of every consumer when the provider value changes. Redux dispatching updates only the subscribed selectors.
- Complexity: Context is straightforward but can become tangled if you mix state and UI logic. Redux enforces a clear separation of concerns via actions, reducers, and selectors.
- Scalability: For a handful of UI flags (theme, locale, auth token), Context keeps the codebase light. For business data that drives many components, Redux’s store and selector memoization prevent unnecessary renders.
- Team ergonomics: If your team already uses Redux, adding a second pattern can increase cognitive load. Conversely, a small project might benefit from the simplicity of Context.
Concrete Implementation & Validation
1️⃣ Counter with React Context
Create a CounterContext that holds a numeric value and an updater function. Memoize the value to avoid re‑renders.
// counter-context.tsx
import { createContext, useContext, useState, useMemo } from "react";
interface CounterState { count: number; setCount: (c: number) => void; }
export const CounterContext = createContext<CounterState | undefined>(undefined);
export const CounterProvider: React.FC = ({ children }) => {
const [count, setCount] = useState(0);
const value = useMemo(() => ({ count, setCount }), [count]);
return {children};
};
export const useCounter = () => {
const ctx = useContext(CounterContext);
if (!ctx) throw new Error("useCounter must be used within CounterProvider");
return ctx;
};
2️⃣ Counter with Redux Toolkit
Define a slice and expose a selector.
// counterSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 },
reducers: {
increment: state => { state.count += 1; },
set: (state, action: PayloadAction<number>) => { state.count = action.payload; },
},
});
export const { increment, set } = counterSlice.actions;
export default counterSlice.reducer;
export const selectCount = (state: { counter: { count: number } }) => state.counter.count;
3️⃣ Shared UI
Wrap the app with both providers to illustrate a boundary: Context for UI settings (theme) and Redux for business data (counter).
// App.tsx
import { CounterProvider } from './counter-context';
import { Provider } from 'react-redux';
import store from './store';
import CounterDisplay from './CounterDisplay';
const App: React.FC = () => (
);
4️⃣ Validation Steps
- Render counts: Use React DevTools Profiler to record how many times
CounterDisplayre‑renders when the counter changes. Verify that the Context version re‑renders on every update, while the Redux version only re‑renders when the selector value changes. - Bundle size: Run
npm run build, thennpx source-map-explorer build/static/js/main.*.js. Compare the size of the bundle with and without Redux Toolkit. Expect an increase of ~12 kB gzipped. - Redux DevTools: Open the browser extension, dispatch the
incrementaction, and confirm that the action log and state snapshot update correctly. - Context memoization test: Replace
useMemowith a plain object and observe the Profiler; re‑renders should spike, confirming the memoization benefit.
Practical Decision Checklist
- If the shared data is infrequent and small (theme, locale), use Context.
- If the data drives many components, updates often, or requires derived state, use Redux.
- Prefer Redux when you need middleware, async flows, or time‑travel debugging.
- Keep Context and Redux separate; avoid mixing them for the same piece of state.
- Always memoize Context values or split them into multiple providers to limit re‑renders.
Conclusion
React Context and Redux are not mutually exclusive; they serve different roles. By evaluating the size, update frequency, and tooling needs of your shared state, you can choose the pattern that delivers the best performance and developer experience for your React 18 application.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.