---
title: "React Context API for State Management"
description: "A practical look at the React Context API for state management, including how to build a simple shared state system with Next.js and TypeScript."
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/blog/post/react-context-api-state-management
---

# React Context API for State Management

## Introduction

Managing application state well can make or break a React project. React offers several options for state management, and the Context API is one of the most flexible. But what exactly is the React Context API, and how does it differ from Redux, another popular state management library? This post covers both questions, along with the capabilities and limitations of the React Context API.

## What is Context API in React?

### Understanding the fundamentals of Context API

The React Context API arrived in React 16.3. It gives you a way to share data between components without manually passing props through every level of the component tree. That matters most when deeply nested components need shared data, like user authentication status, application themes, or language preferences.

The Context API is built on two core components:

1. ``: This component makes data available to all descendant components. It accepts a value prop, which can be any data type you want to share, including objects or functions.
2. ``: The Consumer component reads the data provided by the nearest `` in the component hierarchy.

Data shared through Context API looks like props, but it is available globally within the context. Any component that needs it can read it directly, with no explicit prop passing from parent to child.

### When should you use Context API?

You might be wondering why Context API should be your choice over traditional prop-passing. There are several situations where it earns its place:

1. **Eliminating prop drilling**: In large, deeply nested component trees, manually passing props down multiple levels gets unwieldy and error-prone. Context API gives you one place to manage shared data instead.
2. **Global state management**: When your application needs to read and change data from several places, Context API lets you set up a global state that's easy to maintain and update.
3. **Themes and localization**: Context API is a good fit for themes, user preferences, and localization settings, since those are usually needed in several sections of your application.
4. **Authentication**: If you need to retain user authentication status and make it accessible to different parts of your application, Context API offers an effective solution.

## Building a simple state management system with Context API

### Creating a new Next.js project

To demonstrate the capabilities of Context API, we'll build a simple state management system using Next.js. First, let's create a new Next.js project by running the following command:

```shell title="Terminal"
# npm
npx create-next-app next-context-api

# yarn
yarn create next-app next-context-api

# pnpm
pnpx create-next-app next-context-api
```

Next, the command-line interface will prompt you to select a template for your project. For this tutorial, we'll choose `TypeScript` as our preferred option.

```shell title="Terminal"
? Would you like to use TypeScript? › No / Yes # Yes
? Would you like to use ESLint? › No / Yes # Yes
? Would you like to use Tailwind CSS? › No / Yes # Yes
? Would you like to use `src/` directory? › No / Yes # Yes
? Would you like to use App Router? (recommended) › No / Yes # Yes
? Would you like to customize the default import alias (@/*)? › No / Yes # Yes
? What import alias would you like configured? › @/* # keep the default
```

We'll also install one additional dependency:

```shell title="Terminal"
# npm
npm install --save-dev prettier prettier-plugin-tailwindcss

# yarn
yarn add --D prettier prettier-plugin-tailwindcss

# pnpm
pnpm add --save-dev prettier prettier-plugin-tailwindcss
```

Once the project is created, navigate to the project directory and start the development server by running the following command:

```shell title="Terminal"
# npm
npm run dev

# yarn
yarn dev

# pnpm
pnpm dev
```

### Cleaning up the project and organizing the file structure

Next, let's clean up the project by removing the default files and folders that we won't be using. We'll also create a new folder structure to organize our project files.

```shell title="Project Structure"
Root
├── src
│   ├── app
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── assets
│   │   ├── icons
│   │   │   └── favicon.ico
│   │   └── styles
│   │       └── globals.css
│   ├── components
│   │   ├── shared-state-child
│   │   │   └── index.tsx
│   │   ├── shared-state-grand-child
│   │   │   └── index.tsx
│   │   ├── shared-state-sibling
│   │   │   └── index.tsx
│   │   index.ts
│   └── providers
│       └── use-provider.tsx
├── .eslintrc.cjs
├── .gitignore
├── .npmrc
├── .nvmrc
├── .prettierrc.cjs
├── .yarnrc
├── next.config.mjs
├── package.json
├── postcss.config.cjs
├── README.md
├── tailwind.config.ts
├── tsconfig.json
└── yarn.lock
```

<br />

  You can find the starter code for this project on [Starter
  Code](https://github.com/MKAbuMattar/next-context-api/tree/starter-code)
  branch.{' '}

### Creating a custom provider for Context API

Now, let's create a custom provider for our Context API. First, we'll create a new file called `use-provider.tsx` inside the `providers` folder. Then, we'll add the following code to this file:

```tsx title="~/src/providers/use-provider.tsx"
'use client';

  type ReactNode,
  type Context,
  createContext,
  useContext,
  useState,
} from 'react';

const initialContext = () => new Map<string, T>();
const Context = createContext(initialContext());

type ProviderProps = {
  children: ReactNode;
};

  {children}
);

const useContextProvider = (key: string) => {
  const context = useContext(Context);
  return {
    set value(v: T) {
      context.set(key, v);
    },
    get value() {
      if (!context.has(key)) {
        throw Error(`Context key '${key}' Not Found!`);
      }
      return context.get(key) as T;
    },
  };
};

  const provider = useContextProvider>(key);
  if (initialValue !== undefined) {
    const Context = createContext(initialValue);
    provider.value = Context;
  }
  return useContext(provider.value);
};

  let state = undefined;
  if (initialValue !== undefined) {
    const _useState = useState;
    state = _useState(initialValue);
  }
  return useProvider(key, state);
};
```

Let's walk through the code above to see how it works. First, we create a new context using the `createContext` function. Then, we create a custom hook called `useProvider` that accepts two arguments: `key` and `initialValue`. The `key` argument is used to identify the context, while the `initialValue` argument is used to set the initial value of the context. Next, we create a custom hook called `useSharedState` that accepts the same arguments as the `useProvider` hook. This hook is used to create a shared state that can be accessed and modified by multiple components.

### Using the custom provider in the application

Now, let's use the custom provider we created in the previous step in our application. First, we'll import the `Provider` component from the `use-provider.tsx` file. Then, we'll wrap the `Layout` component with the `Provider` component. Finally, we'll add the following code to the `Layout` component:

```tsx title="~/src/app/layout.tsx" ins={9-10} ins={24} ins={26}

// Context API

const inter = Inter({subsets: ['latin']});

  title: 'Next.js Context API',
  description: 'Next.js Context API example with TypeScript to manage state.',
};

type RootLayoutProps = {
  children: ReactNode;
};

  return (
    <html lang={'en'}>

        <body className={inter.className}>{children}</body>

    </html>
  );
}
```

### Creating a shared state

Now, let's create a shared state using the `useSharedState` hook. First, we'll create a new file called `index.tsx` inside the `components/shared-state-child` folder. Then, we'll add the following code to this file:

```tsx title="~/src/components/shared-state-child/index.tsx"
'use client';

// components

// Context API

  const [count] = useSharedState<number>('count');

  return (

      <p className={'text-center text-xl font-semibold'}>
        Shared State Child: {count}
      </p>

  );
};

```

Next, we'll create a new file called `index.tsx` inside the `components/shared-state-grand-child` folder. Then, we'll add the following code to this file:

```tsx title="~/src/components/shared-state-grand-child/index.tsx"
'use client';

// Context API

  const [count] = useSharedState<number>('count');

  return (

      <p className={'text-center text-xl font-semibold'}>
        Shared State Grand Child: {count}
      </p>

  );
};

```

Finally, we'll create a new file called `index.tsx` inside the `components/shared-state-sibling` folder. Then, we'll add the following code to this file:

```tsx title="~/src/components/shared-state-sibling/index.tsx"
'use client';

// Context API

  const [count] = useSharedState<number>('count');

  return (

      <p className={'text-center text-xl font-semibold'}>
        Shared State Sibling: {count}
      </p>

  );
};

```

Creating a `index.ts` file inside the `components` folder and adding the following code to it:

```tsx title="~/src/components/index.ts"

```

### Updating the shared state from the parent component or page

Now, let's update the shared state from the parent component. First, we'll create a new file called `index.tsx` inside the `app` folder. Then, we'll add the following code to this file:

```tsx title="~/src/app/page.tsx" ins={1} ins={3-4} ins={6-7} ins={10} ins={12-14} ins={20-22} ins={24-56}
'use client';

// Context API
// components

  const [_, setCount] = useSharedState<number>('count', 0);

  const increment = () => setCount((prev) => prev + 1);
  const decrement = () => setCount((prev) => prev - 1);
  const reset = () => setCount(0);

  return (
    <main className={'flex h-screen flex-col items-center justify-center'}>
      <h1 className={'text-center text-4xl font-bold'}>Next.js Context API</h1>

      <p className={'text-center text-xl font-semibold'}>
        Count Example with Context API and TypeScript
      </p>

      <div className={'mt-8 flex flex-col items-center justify-center gap-4'}>

        <div className={'flex flex-row items-center justify-center gap-4'}>
          <button
            type={'button'}
            className={
              'rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700'
            }
            onClick={increment}
          >
            Increment
          </button>
          <button
            type={'button'}
            className={
              'rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700'
            }
            onClick={decrement}
          >
            Decrement
          </button>
          <button
            type={'button'}
            className={
              'rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700'
            }
            onClick={reset}
          >
            Reset
          </button>
        </div>
      </div>
    </main>
  );
}
```

### Testing the application

Finally, let's test the application by running the following command:

```shell title="Terminal"
# npm
npm run dev

# yarn
yarn dev

# pnpm
pnpm dev
```

If everything works as expected, you should see the following output:

![Next.js Context API Example](/assets/blog/0060-react-context-api-state-management/next-context-api-example.png)

  You can find the final code for this project on [Final
  Code](https://github.com/MKAbuMattar/next-context-api) branch.

## Is Context API the same as Redux?

### React Context API compared with Redux

Redux is a well-known state management library, and plenty of React applications use it. It gives you a structured, centralized way to manage application state. So is Context API just a Redux alternative? Here are the real differences between the two.

1. **Complexity**: Redux is known for strict architectural rules, and that cuts both ways. It enforces one-directional data flow and requires actions and reducers. That helps larger applications and feels like overkill on smaller projects. Context API is lighter and more flexible, with a simpler entry point, which suits applications with modest state management needs.

2. **Ecosystem**: Redux has a mature ecosystem with many extensions, middleware, and developer tools. It has been tested hard in the field and has a large community with answers for most problems. Context API is gaining popularity, but its ecosystem is not as broad. If you need the more complete toolset, Redux is still the preferred choice.

3. **Performance**: Redux does well on performance through memoization and efficient state updates. Context API on its own does not optimize as much. Bring in memoization helpers like reselect and useMemo, though, and you can get solid performance out of Context API too.

4. **Learning curve**: Redux has a steeper learning curve because of its strict conventions and the boilerplate that comes with them. Context API is more approachable, especially for developers new to state management in React. If you want something quick and uncomplicated, Context API is the one to reach for.

5. **State size**: For applications with large, tangled state structures, Redux gives you a clear, structured approach through reducers and actions. Context API fits applications with smaller and simpler state management needs.

**Picking the right tool**

The choice between Context API and Redux depends on what your application actually demands. On a small to medium project where you want simplicity and a short learning curve, Context API is a strong choice. For large applications with complex state management needs, where a mature ecosystem earns its keep, Redux is still the better option. Sometimes a mix works best: Context API for simpler local state inside specific components, Redux for the overall application state.

## What is the problem with Context API in React?

### Understanding the limitations of Context API

The React Context API has real limitations for state management. Here are the challenges you're likely to run into when using it.

1. **Propagation of updates**: Context API re-renders every component consuming the context each time the provider's value changes. With a deep component tree, that means re-renders you didn't need. Memoization and component-level optimization ease it.

2. **No built-in middleware**: Redux offers middleware for managing side effects and asynchronous actions, which many applications need. Context API has no built-in middleware, so you either add libraries or write your own handling for side effects.

3. **Debugging tools**: Redux offers an extensive suite of developer tools that pay off when you are debugging. Context API has some developer tools, but not the same depth, so tracing data flow and debugging issues is harder.

4. **Global vs. local state**: Context API is mainly designed for sharing global state. If your application needs components with local state that shouldn't be shared with the whole application, that is less straightforward with Context API. Redux, which can handle local component state, gives you more control there.

5. **Handling complex state**: For applications with complex state structures, Redux's reducers and actions offer a clear and structured approach. With Context API you write more code to manage complex state well.

## Frequently Asked Questions on Context API

Now that we've explored the fundamentals, compared Context API with Redux, and discussed its limitations, let's address some common questions related to the React Context API:

### Can Context API replace Redux for large applications?

It is technically possible, but Context API is usually not the best fit for large applications. Redux's architecture, middleware, and developer tools are better equipped to handle the complexity often encountered in large applications.

### Can Context API and Redux coexist in the same application?

Yes, you can use both Context API and Redux in a single application. Context API handles simpler local state inside specific components, while Redux takes care of global state and complex state structures.

### What are some typical use cases for Context API?

Context API works well for global application state: user authentication, theme management, and localization. It also removes the need for prop drilling in deeply nested component structures.

### Has Redux become obsolete now that Context API exists?

Redux has not become obsolete. It is still a valuable tool, particularly for large applications with complicated state management requirements. Context API is lighter and friendlier to beginners, but it is an alternative rather than a replacement.

### Can functions and methods be shared via Context API?

Yes. Context API lets you share functions and methods, so you can pass behavior across components as well as data.

## Conclusion

The React Context API is a solid addition to React's state management options. It simplifies sharing data between components, removes prop drilling, and manages global application state efficiently. It won't replace Redux in every case, but it's a more accessible and lighter alternative, especially for smaller projects and simpler state management needs.

Knowing the strengths and limitations of each tool matters. Weigh your project's requirements and pick accordingly, whether that's Context API, Redux, or a combination of both.

## References

- [React Context API - Official Documentation](https://react.dev/learn/passing-data-deeply-with-context)
- [Redux - Official Documentation](https://redux.js.org/)
- [When to Use React Context vs. Redux - LogRocket Blog](https://blog.logrocket.com/react-context-api-vs-redux/)
- [A Guide to React Context API - freeCodeCamp](https://www.freecodecamp.org/news/react-context-api-a-complete-guide/)
- [Managing State in React with Context API and Hooks - Tania Rascia](https://www.taniarascia.com/using-context-api-in-react/)
- [Next.js Documentation - State Management](https://nextjs.org/docs/pages/building-your-application/data-fetching/forms-and-mutations#managing-form-state) (Note: Next.js docs might not directly cover Context API in depth for global state, but it's relevant for Next.js projects)
- [TypeScript with React Context API - Robin Wieruch](https://www.robinwieruch.de/react-typescript-context/)
- [Understanding `useContext` Hook in React - DigitalOcean](https://www.digitalocean.com/community/tutorials/react-usecontext)
- [React Context API vs Redux: Which One Should You Choose? - Simform](https://www.simform.com/blog/react-context-api-vs-redux/)
- [State Management with React Context and Hooks - Kent C. Dodds](https://kentcdodds.com/blog/application-state-management-with-react)
- [Avoiding Prop Drilling in React with Context API - Medium](https://medium.com/swlh/avoiding-prop-drilling-in-react-with-context-api-8c00768e95e)
- [Performance Considerations for React Context API - (Example: Search for articles on this topic from reputable sources like LogRocket, Smashing Magazine, or dev.to)](https://www.developerway.com/posts/how-to-write-performant-react-apps-with-context)
