DEV Community

Cover image for React Context API: The Best Way to Manage Global State?
Shefali
Shefali

Posted on • Edited on • Originally published at shefali.dev

React Context API: The Best Way to Manage Global State?

In React applications, state management is an important concept. When the process of passing props from parent to child components gets complex, which is known as props drilling, then you can use React Context API to make this process simpler.

In this post, you’ll learn:

  • Why state management is crucial in React.
  • What prop drilling is and its downsides.
  • How to use React Context API step by step.
  • Performance optimizations and best practices.

Before we get started, don’t forget to subscribe to my newsletter!
Get the latest tips, tools, and resources to level up your web development skills delivered straight to your inbox. Subscribe here!

Now let’s jump right into it!🚀

Why is State Management Important in React?

State management is important in React to keep different components in sync. In small applications, passing state from a parent component to a child component is simple. But as your app grows, you may need to share state between multiple components that are not related. This makes the process inefficient.

When state is passed at multiple levels inside a component, this is called props drilling.

For example:

const Parent = () => {
  const user = { name: "John" };
  return <Child user={user} />;
};

const Child = ({ user }) => {
  return <GrandChild user={user} />;
};

const GrandChild = ({ user }) => {
  return <p>Hello, {user.name}!</p>;
};
Enter fullscreen mode Exit fullscreen mode

Problem:

  • The user is passed down three levels, even though only GrandChild needs it.
  • If more components are added, maintaining this structure becomes difficult.

Solution:

To avoid prop drilling, you can use React Context API, which provides a global store that any component can access directly. This will manage shared data efficiently.


What is React Context API?

React Context API is a built-in state management solution that provides a global store that you can access anywhere within the app and share state globally across components without passing props manually.

Why Use Context API?

Context API is useful because:

  • It avoids prop drilling.
  • This is a lightweight alternative to Redux.
  • Needs no extra set up as it is built in React.
  • Works well for small to medium-sized applications.

How to Use React Context API (Step-by-Step Guide)

Here’s a step-by-step guide to setting up context API:

Step 1: Create a Context

Create a new file UserContext.js and define a context.

import { createContext } from "react";

// Create Context 
export const UserContext = createContext();
Enter fullscreen mode Exit fullscreen mode

Here,

  • createContext() creates a new global context.
  • This context will be used to share data between components.

Tip: Keep your context files in a separate folder within the app for code maintainability.

You might be wondering, “Do I need to memorize this Syntax?”

No worries! You can always refer back to this guide or the official docs whenever needed.

Step 2: Create a Context Provider

Create a context provider in the same file like this:

import { useState } from "react";

export function UserProvider({ children }) {
  const [user, setUser] = useState("John");

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here,

  • useState(“John”) initializes state with the value John.
  • <UserContext.Provider> wraps the app and provides the user state.
  • The value prop makes user and setUser available throughout the app.

Now, your UserContext.js file will look like this:

import { createContext, useState } from "react";

export const UserContext = createContext();

export function UserProvider({ children }) {
  const [user, setUser] = useState("John");

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Wrap the App in the Provider

Modify App.js to wrap the app with UserProvider.

import { UserProvider } from "./UserProvider";
import Home from "./Home";
import About from "./About";

function App() {
  return (
    <UserProvider>
      <Home />
      <About />
    </UserProvider>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Here,

  • The UserProvider wraps the entire application.
  • Now, Home and About can access the user state without prop drilling.

Step 4: Consume the Context in Components

(A) Read Context Data using useContext Hook

import { useContext } from "react";
import { UserContext } from "./UserContext";

function Home() {
  // The value of user is extracted using destructuring
  const { user } = useContext(UserContext);

  return <h1>Welcome, {user}!</h1>;
}

export default Home;
Enter fullscreen mode Exit fullscreen mode

Here,

  • useContext(UserContext) gives access to the global state.
  • user is directly used in the component without passing props.

(B) Updating the State in Context

import { useContext } from "react";
import { UserContext } from "./UserContext";

function About() {
  const { user, setUser } = useContext(UserContext);

  return (
    <div>
      <h1>Current User: {user}</h1>
      <button onClick={() => setUser("David")}>Change User</button>
    </div>
  );
}

export default About;
Enter fullscreen mode Exit fullscreen mode

Here, clicking the button updates the user globally across all components.


Performance Optimization

When the context state gets updated, then all of the connected components get re-rendered, which is unnecessary. To optimize this:

Use useMemo for Context Value

Wrap the state in useMemo to prevent re-renders unless the state actually changes.

const value = useMemo(() => ({ user, setUser }), [user]);
Enter fullscreen mode Exit fullscreen mode

Complete example:

export function UserProvider({ children }) {
  const [user, setUser] = useState("John");

  const value = useMemo(() => ({ user, setUser }), [user]);

  return (
    <UserContext.Provider value={value}>
      {children}
    </UserContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Without useMemo, every re-render of the provider will create a new object for value={{ user, setUser }}; this will cause all the components (which are using the user state) to re-render even if the user hasn’t changed.

Split Contexts for Better Performance

Instead of using a single context for everything, create multiple contexts for different concerns (e.g., user state, theme state).

For example:

<UserProvider>
  <ThemeProvider>
    <App />
  </ThemeProvider>
</UserProvider>
Enter fullscreen mode Exit fullscreen mode

This prevents unnecessary updates when only one state changes.

How Does This Work?

  • UserProvider manages the user state (e.g., authentication, profile details).
  • ThemeProvider manages theme preferences (light/dark mode).
  • The App component and all its children can now access both user data and theme settings without prop drilling.

Creating Theme Context

import { createContext, useState } from "react";

export const ThemeContext = createContext();

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light");

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now, any component inside <ThemeProvider> can access and update the theme.


When to Use Context API?

  • When multiple components are sharing the same state.
  • To avoid prop drilling.
  • To avoid heavy setup of Redux.
  • When you want to manage a global state with a lightweight solution.
  • For managing themes, authentication, or language settings.

Wrapping Up

That’s all for today!

By the way, if you ever need free HTML website templates, I recommend checking out HTMLrev, I use it all the time. And when I’m looking for web design inspiration, Websitevice is one of my go-to resources.

For paid collaboration connect with me at : connect@shefali.dev

If you enjoy my work and want to support what I do:

👉 Become a Patreon supporter
👉 Or buy me a coffee

Every small gesture keeps me going! 💛

Top comments (6)

Collapse
 
kc900201 profile image
KC • Edited

Thank you again for the detailed explanation of state management in React. It would be nice if you could introduce some of the generally used third party libraries as well in React's state management. Example as below:

  1. Redux
  2. useContext (introduced)
  3. Zustand
  4. React's hooks - useState, useReduced

Examples of how each library is used depending on the scale of the project is very recommended

Collapse
 
devshefali profile image
Shefali

Thank you so much for your suggestions. I'll soon write posts about these topics.

Collapse
 
soumensmarttechie profile image
Soumen

Thanks for sharing. Explanation was detailed and upto the point. Useful

Collapse
 
devshefali profile image
Shefali

Thank you so much for your feedback!

Collapse
 
himanshu_code profile image
Himanshu Sorathiya

Very nice explanation

Collapse
 
devshefali profile image
Shefali

Thank you so much!