---
title: "React Context API for State Management"
lang: "en"
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/post/react-context-api-state-management
---

![Blog post image for React Context API for State Management - 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.](/_astro/hero.Dv7VXd7h_28B3Ru.webp)

[Home](/)›[Blog](/blog)›[All Categories](/blog/categories)›[ReactJS](/blog/categories/reactjs)

Blog

[Prev in ReactJSGet Started with Building ReactJS and Docker: A Complete Guide](/blog/post/get-started-with-building-reactjs-and-docker-a-complete-guide)[Next in ReactJSReact With Redux Toolkit](/blog/post/react-with-redux-toolkit)

[ReactJS](/blog/categories/reactjs)[State Management](/blog/categories/state-management)[Frontend Development](/blog/categories/frontend-development)[Next.js](/blog/categories/nextjs)[JavaScript](/blog/categories/javascript)

# React Context API for State Management

[Mohammad Abu Mattar](/authors/mohammad-abu-mattar)Published: 14 Oct 202309 Mins read14 Mins listen

[Markdown for AI(opens in a new tab)](/post/react-context-api-state-management/index.md "Open the plain-Markdown version of this page, for pasting into an AI tool")

TL;DR

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.

Series

[Frontend Essentials](/series/frontend-essentials)3/3

[PreviousReact With Redux Toolkit](/blog/post/react-with-redux-toolkit)

All posts in this series (3)

Blog3

1.  [Setup Nextjs Tailwind CSS Styled Components with TypeScript](/blog/post/setup-nextjs-tailwind-css-styled-components-with-typescript)
2.  [React With Redux Toolkit](/blog/post/react-with-redux-toolkit)
3.  [React Context API for State ManagementYou are here](/blog/post/react-context-api-state-management)

### React Context API for State Management

Contents

[Introduction](#introduction)[What is Context API in React?](#what-is-context-api-in-react)[Understanding the fundamentals of Context API](#understanding-the-fundamentals-of-context-api)[When should you use Context API?](#when-should-you-use-context-api)[Building a simple state management system with Context API](#building-a-simple-state-management-system-with-context-api)[Creating a new Next.js project](#creating-a-new-nextjs-project)[Cleaning up the project and organizing the file structure](#cleaning-up-the-project-and-organizing-the-file-structure)[Creating a custom provider for Context API](#creating-a-custom-provider-for-context-api)[Using the custom provider in the application](#using-the-custom-provider-in-the-application)[Creating a shared state](#creating-a-shared-state)[Updating the shared state from the parent component or page](#updating-the-shared-state-from-the-parent-component-or-page)[Testing the application](#testing-the-application)[Is Context API the same as Redux?](#is-context-api-the-same-as-redux)[React Context API compared with Redux](#react-context-api-compared-with-redux)[What is the problem with Context API in React?](#what-is-the-problem-with-context-api-in-react)[Understanding the limitations of Context API](#understanding-the-limitations-of-context-api)[Frequently Asked Questions on Context API](#frequently-asked-questions-on-context-api)[Can Context API replace Redux for large applications?](#can-context-api-replace-redux-for-large-applications)[Can Context API and Redux coexist in the same application?](#can-context-api-and-redux-coexist-in-the-same-application)[What are some typical use cases for Context API?](#what-are-some-typical-use-cases-for-context-api)[Has Redux become obsolete now that Context API exists?](#has-redux-become-obsolete-now-that-context-api-exists)[Can functions and methods be shared via Context API?](#can-functions-and-methods-be-shared-via-context-api)[Conclusion](#conclusion)[References](#references)

## [Introduction](#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?](#what-is-context-api-in-react)

### [Understanding the fundamentals of Context API](#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.  `<Provider>`: 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.  `<Consumer>`: The Consumer component reads the data provided by the nearest `<Provider>` 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?](#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](#building-a-simple-state-management-system-with-context-api)

### [Creating a new Next.js project](#creating-a-new-nextjs-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:

Terminal

```
1# npm2npx create-next-app next-context-api3
4# yarn5yarn create next-app next-context-api6
7# pnpm8pnpx 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.

Terminal

```
1? Would you like to use TypeScript? › No / Yes # Yes2? Would you like to use ESLint? › No / Yes # Yes3? Would you like to use Tailwind CSS? › No / Yes # Yes4? Would you like to use `src/` directory? › No / Yes # Yes5? Would you like to use App Router? (recommended) › No / Yes # Yes6? Would you like to customize the default import alias (@/*)? › No / Yes # Yes7? What import alias would you like configured? › @/* # keep the default
```

We’ll also install one additional dependency:

Terminal

```
1# npm2npm install --save-dev prettier prettier-plugin-tailwindcss3
4# yarn5yarn add --D prettier prettier-plugin-tailwindcss6
7# pnpm8pnpm 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:

Terminal

```
1# npm2npm run dev3
4# yarn5yarn dev6
7# pnpm8pnpm dev
```

### [Cleaning up the project and organizing the file structure](#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.

Project Structure

```
1Root2├── src3│   ├── app4│   │   ├── layout.tsx5│   │   └── page.tsx6│   ├── assets7│   │   ├── icons8│   │   │   └── favicon.ico9│   │   └── styles10│   │       └── globals.css11│   ├── components12│   │   ├── shared-state-child13│   │   │   └── index.tsx14│   │   ├── shared-state-grand-child15│   │   │   └── index.tsx16│   │   ├── shared-state-sibling17│   │   │   └── index.tsx18│   │   index.ts19│   └── providers20│       └── use-provider.tsx21├── .eslintrc.cjs22├── .gitignore23├── .npmrc24├── .nvmrc25├── .prettierrc.cjs26├── .yarnrc27├── next.config.mjs28├── package.json29├── postcss.config.cjs30├── README.md31├── tailwind.config.ts32├── tsconfig.json33└── yarn.lock
```

  

Note

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](#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:

~/src/providers/use-provider.tsx

```
1'use client';2
3import React, {4  type ReactNode,5  type Context,6  createContext,7  useContext,8  useState,9} from 'react';10
11const initialContext = <T,>() => new Map<string, T>();12const Context = createContext(initialContext());13
14type ProviderProps = {15  children: ReactNode;16};17
18export const Provider = ({children}: ProviderProps) => (19  <Context.Provider value={initialContext()}>{children}</Context.Provider>20);21
22const useContextProvider = <T,>(key: string) => {23  const context = useContext(Context);24  return {25    set value(v: T) {26      context.set(key, v);27    },28    get value() {29      if (!context.has(key)) {30        throw Error(`Context key '${key}' Not Found!`);31      }32      return context.get(key) as T;33    },34  };35};36
37export const useProvider = <T,>(key: string, initialValue?: T) => {38  const provider = useContextProvider<Context<T>>(key);39  if (initialValue !== undefined) {40    const Context = createContext<T>(initialValue);41    provider.value = Context;42  }43  return useContext(provider.value);44};45
46export const useSharedState = <T,>(key: string, initialValue?: T) => {47  let state = undefined;48  if (initialValue !== undefined) {49    const _useState = useState;50    state = _useState(initialValue);51  }52  return useProvider(key, state);53};
```

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](#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:

~/src/app/layout.tsx

```
1import '@/assets/styles/globals.css';2// Context API3import {Provider} from '@/provider/use-provider';4import type {Metadata} from 'next';5import {Inter} from 'next/font/google';6import React, {type ReactNode} from 'react';7
8const inter = Inter({subsets: ['latin']});9
10export const metadata: Metadata = {11  title: 'Next.js Context API',12  description: 'Next.js Context API example with TypeScript to manage state.',13};14
15type RootLayoutProps = {16  children: ReactNode;17};18
19export default function RootLayout({children}: RootLayoutProps) {20  return (21    <html lang={'en'}>22      <Provider>23        <body className={inter.className}>{children}</body>24      </Provider>25    </html>26  );27}
```

### [Creating a shared state](#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:

~/src/components/shared-state-child/index.tsx

```
1'use client';2
3// components4import {SharedStateGrandChild} from '@/components';5// Context API6import {useSharedState} from '@/provider/use-provider';7import React, {Fragment} from 'react';8
9export const SharedStateChild = () => {10  const [count] = useSharedState<number>('count');11
12  return (13    <Fragment>14      <p className={'text-center text-xl font-semibold'}>15        Shared State Child: {count}16      </p>17      <SharedStateGrandChild />18    </Fragment>19  );20};21
22export default SharedStateChild;
```

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:

~/src/components/shared-state-grand-child/index.tsx

```
1'use client';2
3// Context API4import {useSharedState} from '@/provider/use-provider';5import React, {Fragment} from 'react';6
7export const SharedStateGrandChild = () => {8  const [count] = useSharedState<number>('count');9
10  return (11    <Fragment>12      <p className={'text-center text-xl font-semibold'}>13        Shared State Grand Child: {count}14      </p>15    </Fragment>16  );17};18
19export default SharedStateGrandChild;
```

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:

~/src/components/shared-state-sibling/index.tsx

```
1'use client';2
3// Context API4import {useSharedState} from '@/provider/use-provider';5import React, {Fragment} from 'react';6
7export const SharedStateSibling = () => {8  const [count] = useSharedState<number>('count');9
10  return (11    <Fragment>12      <p className={'text-center text-xl font-semibold'}>13        Shared State Sibling: {count}14      </p>15    </Fragment>16  );17};18
19export default SharedStateSibling;
```

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

~/src/components/index.ts

```
1export {default as SharedStateChild} from '@/components/shared-state-child';2export {default as SharedStateGrandChild} from '@/components/shared-state-grand-child';3export {default as SharedStateSibling} from '@/components/shared-state-sibling';
```

### [Updating the shared state from the parent component or page](#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:

~/src/app/page.tsx

```
1'use client';2
3// Context API4// components5import {SharedStateChild, SharedStateSibling} from '@/components';6import {useSharedState} from '@/provider/use-provider';7
8export default function Home() {9  const [_, setCount] = useSharedState<number>('count', 0);10
11  const increment = () => setCount((prev) => prev + 1);12  const decrement = () => setCount((prev) => prev - 1);13  const reset = () => setCount(0);14
15  return (16    <main className={'flex h-screen flex-col items-center justify-center'}>17      <h1 className={'text-center text-4xl font-bold'}>Next.js Context API</h1>18
19      <p className={'text-center text-xl font-semibold'}>20        Count Example with Context API and TypeScript21      </p>22
23      <div className={'mt-8 flex flex-col items-center justify-center gap-4'}>24        <SharedStateChild />25        <SharedStateSibling />26        <div className={'flex flex-row items-center justify-center gap-4'}>27          <button28            type={'button'}29            className={30              'rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700'31            }32            onClick={increment}33          >34            Increment35          </button>36          <button37            type={'button'}38            className={39              'rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700'40            }41            onClick={decrement}42          >43            Decrement44          </button>45          <button46            type={'button'}47            className={48              'rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700'49            }50            onClick={reset}51          >52            Reset53          </button>54        </div>55      </div>56    </main>57  );58}
```
