React Context API vs Redux: When and How to Use Each

by Didin J. on Oct 08, 2025 React Context API vs Redux: When and How to Use Each

Learn the key differences between React Context API and Redux Toolkit with practical examples, pros and cons, and expert tips for managing state efficiently.

State management is one of the most important aspects of building React applications. As your app grows, managing data flow between components can quickly become complicated — especially when multiple components need access to the same state. That’s where tools like React Context API and Redux come in.

Both solutions aim to make state sharing easier, but they do so in different ways. The React Context API is a built-in solution for passing data through the component tree without prop drilling. Redux, on the other hand, is an external library designed for predictable and centralized state management, often used in larger applications.

In this tutorial, you’ll learn the practical differences between Context API and Redux by building two small apps side by side — one using each approach. By the end, you’ll understand:

  • How Context API and Redux handle state.

  • When to use each approach depending on your app’s size and complexity.

  • The pros and cons of both tools through real code examples.

Whether you’re a beginner exploring state management for the first time or looking to decide which tool suits your next project, this guide will give you a clear, hands-on comparison of React Context API vs Redux.



Prerequisites

Before diving into the examples, make sure you have the following tools and knowledge ready. This tutorial is designed for beginners who already have some basic experience with React and TypeScript.

What You Should Know

To get the most out of this tutorial, you should be familiar with:

  • The fundamentals of React components and hooks (useState, useEffect, and useContext).

  • Basic TypeScript syntax for defining types and interfaces.

  • How to run a React app using the command line.

If you’re completely new to React, check out the official React documentation before proceeding.

What You’ll Need Installed

Make sure you have the following installed on your system:

  • Node.js (v18 or higher) – Download from nodejs.org.

  • npm or yarn – Comes bundled with Node.js.

  • A code editor – Visual Studio Code is recommended.

  • A modern browser – Preferably Chrome, Firefox, or Edge for running and testing the app.

Project Overview

In this tutorial, you’ll create two simple counter applications:

  1. One uses React Context API for state management.

  2. Another is using Redux Toolkit, the modern way to implement Redux in React.

This side-by-side comparison will help you understand how each approach handles state updates, component communication, and scalability.



Setting Up the Project

To keep things simple and modern, we’ll use Vite to quickly scaffold our React 19.2 + TypeScript project. Vite is fast, lightweight, and works perfectly with the latest React features.

Step 1: Create a New React App with Vite

Open your terminal and run the following command to create a new project:

npm create vite@latest react-context-vs-redux -- --template react-ts

The Vite + React application will run automatically. Then open the URL shown in the terminal (usually http://localhost:5173/) in your browser.

React Context API vs Redux: When and How to Use Each - vite + react

You should see the default Vite + React welcome page. Stop the running Vite + React application, then move into the project folder:

cd react-context-vs-redux

This command sets up a new React project with TypeScript support out of the box.

Step 2: Install Dependencies

We’ll first install dependencies that are common for both examples:

npm install

Next, install the Redux Toolkit and React-Redux (for the Redux section later):

npm install @reduxjs/toolkit react-redux

Step 3: Project Structure Overview

After installation, your folder structure should look like this:

react-context-vs-redux/
├── node_modules/
├── public/
├── src/
│   ├── assets/
│   ├── App.css
│   ├── App.tsx
│   ├── main.tsx
│   └── vite-env.d.ts
├── index.html
├── package.json
├── tsconfig.json
└── vite.config.ts

We’ll create two folders inside src/ — one for each example:

src/
├── context-example/
├── redux-example/

Each folder will contain its own components, store (if applicable), and context files, so you can easily compare how Context API and Redux manage state.



Managing State with React Context API

The React Context API is a built-in feature that allows you to share data across components without passing props manually at every level (known as prop drilling).

For small to medium apps, Context API is often all you need to manage global state effectively.

In this section, you’ll build a simple counter app using Context API and the useReducer hook — similar to Redux logic but simpler and built into React.

Step 1: Create the Context

Inside src/context-example/, create a file named CounterContext.tsx:

import React, {
  createContext,
  useReducer,
  type ReactNode,
  type Dispatch
} from "react";

type State = {
  count: number;
};

type Action = { type: "INCREMENT" } | { type: "DECREMENT" } | { type: "RESET" };

const initialState: State = { count: 0 };

function counterReducer(state: State, action: Action): State {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + 1 };
    case "DECREMENT":
      return { count: state.count - 1 };
    case "RESET":
      return { count: 0 };
    default:
      return state;
  }
}

type CounterContextType = {
  state: State;
  dispatch: Dispatch<Action>;
};

export const CounterContext = createContext<CounterContextType | undefined>(
  undefined
);

export const CounterProvider = ({ children }: { children: ReactNode }) => {
  const [state, dispatch] = useReducer(counterReducer, initialState);
  return (
    <CounterContext.Provider value={{ state, dispatch }}>
      {children}
    </CounterContext.Provider>
  );
};

What’s happening here:

  • We define our state and actions using TypeScript types.

  • The useReducer hook manages state updates based on dispatched actions.

  • We wrap everything in a CounterProvider component, which provides state to any child components.

Step 2: Create the Counter Component

Now, inside the same folder, create Counter.tsx:

import React, { useContext } from "react";
import { CounterContext } from "./CounterContext";

const Counter = () => {
  const context = useContext(CounterContext);

  if (!context) {
    throw new Error("Counter must be used within a CounterProvider");
  }

  const { state, dispatch } = context;

  return (
    <div style={{ textAlign: "center", marginTop: "2rem" }}>
      <h2>React Context API Counter</h2>
      <h3>Count: {state.count}</h3>
      <button onClick={() => dispatch({ type: "INCREMENT" })}>Increment</button>
      <button onClick={() => dispatch({ type: "DECREMENT" })}>Decrement</button>
      <button onClick={() => dispatch({ type: "RESET" })}>Reset</button>
    </div>
  );
};

export default Counter;

Explanation:

  • The component is used useContext to access the global counter state and dispatch actions.

  • When a button is clicked, it sends an action (INCREMENT, DECREMENT, or RESET) to update the state.

Step 3: Integrate the Provider into App

Open src/App.tsx and replace its contents with:

import React from "react";
import { CounterProvider } from "./context-example/CounterContext";
import Counter from "./context-example/Counter";

function App() {
  return (
    <CounterProvider>
      <Counter />
    </CounterProvider>
  );
}

export default App;

How it works:

  • The CounterProvider wraps the Counter component, making the shared state accessible to it.

  • You can now add more components anywhere inside CounterProvider, and they’ll all share the same counter state.

Step 4: Run the App

Run the development server again:

npm run dev

Then open your browser and navigate to http://localhost:5173/.

React Context API vs Redux: When and How to Use Each - counter using context api

You’ll see your React Context API Counter App in action — increment, decrement, and reset the count. 🎉

Pros and Cons of Context API

✅ Pros

  • Built into React — no extra library needed.

  • Simple setup and minimal boilerplate.

  • Great for small and medium projects.

⚠️ Cons

  • Not optimized for large or deeply nested applications.

  • Frequent re-renders can affect performance if used for high-frequency updates.

  • Lacks advanced features like devtools or middleware.



Managing State with Redux (Redux Toolkit)

While React Context API is great for smaller projects, Redux Toolkit (RTK) shines when your app grows larger and you need predictable, centralized state management.

Redux Toolkit is the official, modern way to use Redux. It eliminates most of the old boilerplate and adds convenient tools for reducers, actions, and async logic.

In this section, you’ll recreate the same counter app using Redux Toolkit and compare it with the Context version.

Step 1: Create a Redux Slice

Inside src/redux-example/, create a file named counterSlice.ts:

import { createSlice } from "@reduxjs/toolkit";

type CounterState = {
  value: number;
};

const initialState: CounterState = {
  value: 0,
};

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

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

What’s happening here:

  • createSlice() automatically generates action creators and reducer logic.

  • State updates can directly modify the state thanks to Immer, which Redux Toolkit uses internally.

  • We export both the reducer (for the store) and the actions (for the UI).

Step 2: Configure the Redux Store

Now, create a new file in the same folder called store.ts:

import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counterSlice";

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

Explanation:

  • The store combines all reducers (in this case, just counter).

  • We export RootState and AppDispatch types for strong TypeScript support throughout the app.

Step 3: Connect Redux to React

Open src/main.tsx and wrap your app with the Redux Provider component:

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
import type { Provider } from "react-redux";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </StrictMode>
);

What this does:

  • The Provider makes the Redux store available to all React components inside your app.

Step 4: Create the Redux Counter Component

Inside src/redux-example/, create Counter.tsx:

import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { RootState, AppDispatch } from "./store";
import { increment, decrement, reset } from "./counterSlice";

const Counter = () => {
  const count = useSelector((state: RootState) => state.counter.value);
  const dispatch = useDispatch<AppDispatch>();

  return (
    <div style={{ textAlign: "center", marginTop: "2rem" }}>
      <h2>Redux Toolkit Counter</h2>
      <h3>Count: {count}</h3>
      <button onClick={() => dispatch(increment())}>Increment</button>
      <button onClick={() => dispatch(decrement())}>Decrement</button>
      <button onClick={() => dispatch(reset())}>Reset</button>
    </div>
  );
};

export default Counter;

Explanation:

  • useSelector extracts data from the Redux store.

  • useDispatch sends actions to update the state.

  • Each button dispatches an action created automatically by Redux Toolkit.

Step 5: Render the Redux Counter App

Modify your src/App.tsx to use the Redux version:

import React from "react";
import Counter from "./redux-example/Counter";

function App() {
  return <Counter />;
}

export default App;

Then start the app again:

npm run dev

Open your browser at http://localhost:5173/, and you’ll see the Redux Toolkit Counter App in action.

Pros and Cons of Redux Toolkit

✅ Pros

  • Centralized, predictable state management.

  • Scales easily across large apps.

  • Powerful DevTools and middleware support.

  • Built-in support for async operations (createAsyncThunk).

⚠️ Cons

  • More setup compared to Context API.

  • Overkill for small or simple apps.

  • Requires basic understanding of Redux patterns.



Context API vs Redux — Head-to-Head Comparison

Both React Context API and Redux Toolkit help you manage state globally across components — but they differ in setup, scalability, and developer experience.

Let’s compare them side by side to understand when each one fits best.

🔍 1. Setup and Complexity

Criteria Context API Redux Toolkit
Setup Built-in, no extra libraries needed Requires installing Redux Toolkit and React-Redux
Boilerplate Minimal Slightly more setup (store, slice, provider)
Learning curve Easy for beginners Moderate — requires understanding Redux concepts

Verdict: Context API wins for simplicity and small apps. Redux Toolkit adds structure, which pays off in larger apps.

⚙️ 2. Performance and Scalability

Criteria Context API Redux Toolkit
Re-renders Can cause unnecessary re-renders Optimized with selector-based updates
Scalability Suitable for small to medium apps Ideal for large, complex applications
State organization Shared across one provider Modular — state split into slices

Verdict: Redux Toolkit handles large-scale apps better, especially with frequent updates or complex state trees.

🧩 3. Developer Experience

Criteria Context API Redux Toolkit
DevTools None (manual debugging) Excellent Redux DevTools integration
Async logic Must use useEffect or custom hooks Built-in support with createAsyncThunk
Code maintainability Can get messy as app grows Enforces clear structure and separation of concerns

Verdict: Redux Toolkit provides a smoother experience for debugging, async workflows, and team collaboration.

🚀 4. Real-World Use Cases

Scenario Recommended Approach
Small personal projects or prototypes ✅ Context API
Medium-sized apps with simple data flow ✅ Context API or light Redux Toolkit
Large enterprise apps with complex state logic 🚀 Redux Toolkit
Apps requiring async actions (API calls, caching) 🚀 Redux Toolkit
Components that rarely share state ⚙️ Local state or Context API

🧠 Key Takeaways

  • Use Context API when:

    • Your app’s state is simple and doesn’t require advanced features.

    • You want to avoid extra dependencies.

    • Performance isn’t heavily impacted by re-renders.

  • Use Redux Toolkit when:

    • Your app involves multiple domains or async data fetching.

    • You need powerful debugging and time-travel capabilities.

    • You’re working in a team and want consistent, predictable state management.

💡 Pro Tip

For very large applications, it’s common to combine both:

  • Use Context API for UI-related global state (e.g., theme, language).

  • Use Redux Toolkit for application data (e.g., authentication, API responses, user preferences).

This hybrid approach gives you the best of both worlds — simplicity and scalability.



Best Practices and Tips

When deciding between the React Context API and Redux Toolkit, it’s essential to not only understand their technical differences but also apply them effectively. Here are some best practices and tips to help you make the most of your state management choice:

1. Choose the Right Tool for the Right Use Case

  • Use Context API for simple, local, or theme-based state, such as authentication status, user preferences, or UI themes.

  • Use Redux Toolkit for large, complex, or collaborative applications where you need time-travel debugging, middleware, or predictable global state updates.

2. Keep Contexts Small and Specific

Avoid putting your entire app’s state into a single context. Instead, split contexts by domain (e.g., AuthContext, ThemeContext, SettingsContext) to reduce unnecessary re-renders and improve performance.

3. Use Redux Toolkit for Predictable and Testable Logic

Redux’s strict state immutability and predictable reducer pattern make it ideal for debugging and testing. When building scalable apps, leverage:

  • createSlice() for structured reducers

  • createAsyncThunk() for async API calls

  • configureStore() for cleaner store setup

4. Don’t Overuse Context

Context re-renders every component that consumes it when the value changes. To mitigate performance hits:

  • Use memoization with React.memo and useMemo.

  • Keep context values minimal—store large or frequently updated data in local state instead.

5. Combine Tools When Needed

You don’t have to choose just one. For example:

  • Use Context API for static UI-level state (theme, language).

  • Use Redux Toolkit for dynamic data and complex application logic (API responses, user management).

6. Structure Your State Management

Maintain consistent folder structures for easier scalability:

src/
├── context/
│   └── AppContext.tsx
├── store/
│   ├── counterSlice.ts
│   └── store.ts
├── components/
├── pages/
└── App.tsx

7. Monitor and Optimize Performance

Use the React Developer Tools and Redux DevTools extensions to track state changes and ensure updates are efficient. Always profile large applications for unnecessary re-renders.

Pro Tip:
If you start small, begin with the Context API. As your project grows and state management becomes complex, migrate to Redux Toolkit for better scalability and maintainability.



Conclusion and Final Thoughts

Both the React Context API and Redux Toolkit are powerful tools for managing state in modern React applications. The key lies in understanding when to use each and how to apply them efficiently.

  • The Context API is best suited for smaller or medium-sized projects where simplicity and minimal setup are priorities. It shines in handling lightweight global states such as themes, authentication, or localization.

  • Redux Toolkit, on the other hand, is designed for complex, large-scale applications that demand predictability, middleware support, and developer tools like time-travel debugging.

If you’re just getting started, try implementing the Context API first—it’s built into React and easy to understand. As your project evolves and requires more control over state mutations, async actions, and debugging, you can transition smoothly to Redux Toolkit.

In short:

  • Use Context API for simplicity.

  • Use Redux Toolkit for scalability.

By mastering both, you’ll be able to handle virtually any state management scenario in React with confidence and precision.

Next Steps:

  • Explore Recoil, Zustand, or Jotai if you’re curious about other modern state management libraries.

  • Integrate Redux Persist for persisting Redux state between sessions.

  • Experiment with React Query for server state management alongside client state tools.

You can find the full source code on our GitHub.

That's just the basics. If you need more deep learning about React, you can take the following cheap course:

Thanks!