Day 14: Understanding Context API in React
6 mins read

Day 14: Understanding Context API in React

So far in this series, we’ve worked with React state using useState, useReducer, and even Custom Hooks. But there’s still a problem you might have noticed:

When a piece of state is needed by multiple components at different levels, we end up passing props down multiple layers. This process is called prop drilling, and it can quickly become messy.

πŸ‘‰ This is where the Context API in React comes to the rescue.


What is Context API in React?

The Context API in React is a way to share data globally across components without manually passing props down the tree.

Think of it as a centralized data store that components can directly access, no matter where they are in the component hierarchy.


When to Use Context API in React?

  • When multiple components need the same data (e.g., user authentication, theme, language).
  • To avoid prop drilling (passing props through components that don’t even use them).
  • For global state management in small to medium-sized apps.

Syntax of Context API in React

  1. Create Context const MyContext = React.createContext();
  2. Provide Context
    Wrap a parent component with a Provider and pass the value. <MyContext.Provider value={someValue}> <ChildComponent /> </MyContext.Provider>
  3. Consume Context
    Access the value using useContext. const value = useContext(MyContext);

Example 1: Theme Context with Context API in React

Let’s build a theme switcher using the Context API in React.

import React, { createContext, useContext, useState } from "react";

// 1. Create Context
const ThemeContext = createContext();

// 2. Create Provider Component
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light");

  const toggleTheme = () => {
    setTheme((prev) => (prev === "light" ? "dark" : "light"));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// 3. Consume Context
function ThemedComponent() {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <div className={`p-4 ${theme === "light" ? "bg-white" : "bg-gray-800 text-white"}`}>
      <h2 className="text-lg font-bold mb-2">Context API in React Example</h2>
      <p>Current Theme: {theme}</p>
      <button
        onClick={toggleTheme}
        className="px-3 py-2 bg-blue-500 text-white rounded mt-2"
      >
        Toggle Theme
      </button>
    </div>
  );
}

// Root App
function App() {
  return (
    <ThemeProvider>
      <ThemedComponent />
    </ThemeProvider>
  );
}

export default App;

βœ… Now, any component wrapped inside ThemeProvider can access theme and toggleTheme without prop drilling.


Example 2: User Authentication Context

Imagine you have a logged-in user and need to show their info across multiple components.

import React, { createContext, useContext } from "react";

const UserContext = createContext();

function UserProvider({ children }) {
  const user = { name: "Anand", role: "Developer" };

  return <UserContext.Provider value={user}>{children}</UserContext.Provider>;
}

function Navbar() {
  const user = useContext(UserContext);
  return <p className="p-2">Welcome, {user.name} ({user.role})</p>;
}

function Dashboard() {
  const user = useContext(UserContext);
  return <p className="p-2">Access granted for {user.role}</p>;
}

function App() {
  return (
    <UserProvider>
      <Navbar />
      <Dashboard />
    </UserProvider>
  );
}

export default App;

βœ… The UserProvider supplies user data, and Navbar + Dashboard can access it directly without props. This example demonstrates how to share user authentication details (like username, email, role, etc.) across multiple components without passing props manually.

Step 1: Create Context

const UserContext = createContext();
  • This creates a UserContext object.
  • Think of it as a “data pipe” that can carry user information anywhere inside your app.

Step 2: Provide Context (UserProvider)

function UserProvider({ children }) {
  const user = { name: "Anand", role: "Developer" };

  return (
    <UserContext.Provider value={user}>
      {children}
    </UserContext.Provider>
  );
}
  • The UserProvider component wraps other components and provides the user data.
  • Here, user is just a simple object, but in real apps it could come from:
    • API response (after login),
    • Firebase authentication,
    • JWT token, etc.
  • Whatever value we pass into value={user} becomes available to all child components.

Step 3: Consume Context in Navbar

function Navbar() {
  const user = useContext(UserContext);
  return <p className="p-2">Welcome, {user.name} ({user.role})</p>;
}
  • Here, instead of receiving user as a prop, we use:
const user = useContext(UserContext);
  • useContext(UserContext) instantly gives access to the user object provided by UserProvider.
  • This allows Navbar to display:
Welcome, Anand (Developer)

Step 4: Consume Context in Dashboard

function Dashboard() {
  const user = useContext(UserContext);
  return <p className="p-2">Access granted for {user.role}</p>;
}
  • Same thing again β€” no props needed.
  • Dashboard directly accesses the user info.
  • Output will be:
Access granted for Developer

Step 5: Wrap Everything in App

function App() {
  return (
    <UserProvider>
      <Navbar />
      <Dashboard />
    </UserProvider>
  );
}
  • Both Navbar and Dashboard are wrapped inside UserProvider.
  • This means both can directly access the user data without prop drilling.

How Output Looks

Welcome, Anand (Developer)
Access granted for Developer

Real-World Use Case

In a real app, instead of hardcoding user, you might fetch it from an API:

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

  useEffect(() => {
    // Simulate fetching from backend
    setTimeout(() => {
      setUser({ name: "Anand", role: "Admin" });
    }, 1000);
  }, []);

  return (
    <UserContext.Provider value={user}>
      {children}
    </UserContext.Provider>
  );
}

Now, any component in your app (Navbar, Dashboard, Sidebar, Footer) can access user directly with:

const user = useContext(UserContext);

So the biggest win here is: you don’t have to pass user as a prop from App β†’ Navbar β†’ SubNavbar β†’ Menu β†’ Button.
You just wrap everything in UserProvider and use useContext(UserContext) anywhere.


Benefits of Context API in React

  • Eliminates prop drilling.
  • Provides global state with minimal setup.
  • Works perfectly for small to medium apps.
  • Pairs well with Custom Hooks for reusable contexts.

Limitations of Context API in React

  • Not always suitable for very large applications (where Redux or Zustand may be better).
  • Updating context can re-render all consumers, which may impact performance.

External Link

πŸ”— Learn more from the React official Context API docs.


Wrapping Up

Today we learned:

  • What the Context API in React is.
  • How it helps avoid prop drilling.
  • Built real-world examples with theme switching and user authentication.

πŸ‘‰ In the next blog (Day 15), we’ll explore Handling Forms in React, covering controlled vs uncontrolled inputs and simple validation logic.

Leave a Reply

Your email address will not be published. Required fields are marked *