Context API
Learn how the Context API lets you share global data across a component tree without manually passing props through every layer.
Introduction
React data normally flows in one direction: from a parent component down to its children through props. That works well for most data, but some information — like the current theme, the signed-in user, or the selected language — is needed by components scattered all across the tree, often many layers deep. Passing that value down through every intermediate component just so it can reach the one that actually needs it quickly becomes tedious and error-prone.
The Context API is React's built-in solution for sharing this kind of data without manually threading it through every layer. This lesson introduces the problem conceptually, and shows how to create a context, provide a value, and read it from a deeply nested component — a full lesson on prop drilling itself comes later in this course.
- The prop drilling problem that Context solves, at a conceptual level.
- How to create a context with createContext().
- How to provide a value with a Context.Provider.
- How any nested component can read that value directly, no matter how deep it is.
- When Context is the right tool, and when plain props are simpler.
The Prop Drilling Problem
Imagine a "logged-in user" value that only the innermost component in a deeply nested tree actually needs. Without Context, that value has to be passed as a prop through every component in between, even the ones that never use it themselves — they simply relay it further down.
function App() {
const user = { name: "Amol" };
return <Page user={user} />;
}
function Page({ user }) {
return <Layout user={user} />;
}
function Layout({ user }) {
return <Sidebar user={user} />;
}
function Sidebar({ user }) {
return <UserGreeting user={user} />; // finally used here
}
function UserGreeting({ user }) {
return <p>Hello, {user.name}!</p>;
}Page, Layout, and Sidebar do nothing with user except pass it along — this pattern is called prop drilling. It works, but adding a new layer in the middle, or a second piece of shared data, means editing every component in the chain. This lesson introduces the conceptual problem; a dedicated later lesson explores prop drilling in more depth.
What is the Context API?
Context lets a parent component make a value available to every component below it in the tree, no matter how deeply nested, without passing that value through props at every intermediate level. Any descendant component can "tap into" the context directly and read the value it needs.
Broadcasts a Value
A Provider makes a value available to its entire subtree at once.
Direct Access
Any nested component can read the value directly, skipping every layer in between.
Good for Global Data
Ideal for values needed widely: theme, locale, or the current logged-in user.
Not a Replacement for Props
Context complements props; most component data still flows through regular props.
Creating a Context
A context is created once, using createContext(), typically in its own file so it can be imported wherever it is needed. You can pass a default value, which is used only if a component reads the context without any matching Provider above it.
import { createContext } from "react";
// The argument is the default value, used if no Provider wraps the reader
const UserContext = createContext(null);
export default UserContext;Providing a Value
To actually share a value, wrap part of your component tree in that context's Provider and pass the value as a prop. Every component nested inside the Provider — no matter how deep — can now read that value.
import UserContext from "./UserContext";
function App() {
const user = { name: "Amol" };
return (
<UserContext.Provider value={user}>
<Page />
</UserContext.Provider>
);
}Notice Page no longer needs to accept or forward a user prop at all — the Provider makes user available to anything rendered inside it.
Reading Context in a Deeply Nested Component
Any component inside the Provider can read the shared value using useContext (covered in full detail in the next lesson) — completely skipping Page, Layout, and Sidebar, which no longer mention user at all.
import { useContext } from "react";
import UserContext from "./UserContext";
function Page() {
return <Layout />; // no user prop needed anymore
}
function Layout() {
return <Sidebar />; // no user prop needed anymore
}
function Sidebar() {
return <UserGreeting />; // no user prop needed anymore
}
function UserGreeting() {
const user = useContext(UserContext); // reads the value directly
return <p>Hello, {user.name}!</p>;
}Hello, Amol!Page, Layout, and Sidebar no longer need to know that user exists at all. Only the Provider (at the top) and UserGreeting (at the bottom, where the value is actually used) mention UserContext.
When to Use Context
Context is a powerful tool, but it is not meant to replace props everywhere. It works best for data that is genuinely global to a large part of your application, rather than data that only a couple of nearby components need.
Good fit for Context
- Current theme (light / dark)
- Active locale / language
- The logged-in user's info
- Data needed by many unrelated components
Better fit for plain props
- A value only one or two levels deep
- Data specific to a single feature or form
- Anything only a parent and its direct child share
- Values that change extremely frequently on every keystroke
Reaching for Context on every piece of shared state can make components harder to reuse and trace, since it is no longer obvious from the props alone where a value comes from. Start with plain props, and introduce Context once you notice a value being drilled through several components that do not use it themselves.
Common Mistakes
- Reaching for Context for every piece of state, even values only needed by a component and its direct child.
- Forgetting to wrap the components that need the value in a matching Provider, causing them to fall back to the default value.
- Putting a brand-new object or array literal directly as the Provider's value on every render, causing needless re-renders in consuming components.
- Assuming Context is a full state-management solution — it is a mechanism for passing data down the tree, not a replacement for tools designed to manage complex application state.
Best Practices
- Reserve Context for data that is genuinely needed across many, unrelated parts of the tree.
- Keep each context focused on one concern — a UserContext and a ThemeContext, rather than one giant AppContext holding everything.
- Provide a sensible default value in createContext() for components that might render outside any Provider.
- Memoize the value passed to a Provider (with useMemo) if it is an object or array recreated on every render.
- Continue to use plain props for data that only flows one or two levels down.
Frequently Asked Questions
Does Context replace props entirely?
No. Most data in a React app should still flow through props. Context is reserved for values that are needed broadly across many components, like theme or the logged-in user.
What happens if a component reads a context with no Provider above it?
It receives the default value passed to createContext(). If no default was provided, it receives undefined (or null, depending on what you passed).
Can I have more than one context in an app?
Yes, and it is common practice — separate contexts for theme, the current user, and locale, for example, each kept focused on one concern.
Does updating a context value cause every component in the tree to re-render?
Only components that actually read that context via useContext (or a Consumer) re-render when the value changes — components that do not read it are unaffected.
Is prop drilling always bad?
No. Passing a prop down one or two levels is completely normal and often simpler than introducing Context. Prop drilling becomes a problem mainly when a value has to pass through many components that do not use it themselves — the next lesson on Prop Drilling covers this in depth.
Key Takeaways
- Prop drilling means passing a value through components that do not use it, just to reach one that does.
- createContext() creates a context object that can hold a shared value.
- A Context.Provider makes that value available to its entire subtree.
- Any nested component can read the value directly, without it being passed through every intermediate layer.
- Context suits genuinely global data like theme, locale, or the logged-in user — plain props are simpler for most other cases.
Summary
The Context API gives React a built-in way to share data across a component tree without manually passing it down through every intermediate component. By wrapping part of the tree in a Provider, any nested component can read the shared value directly, keeping components in between clean and unaware of data they never use.
In this lesson, you saw the prop drilling problem Context solves, created a context, provided a value, and read it from a deeply nested component, along with guidance on when Context is the right tool. Next, you will look at useContext in more detail — the Hook you already used here to read a context's value.
- You understand the prop drilling problem at a conceptual level.
- You can create a context with createContext().
- You can provide a value with a Context.Provider.
- You know when Context is appropriate versus when plain props are simpler.