Context API (State Management Intro)
As React apps grow, managing shared state becomes harder. Passing data through multiple layers of components can get messy.
This is where the Context API helps.
The Core Problem: Prop Drilling
Imagine this component structure:
App
└── Layout
└── Sidebar
└── UserProfileNow, suppose App has user data, and UserProfile needs it.:
const user = { name: "John" };Without Context, you must pass it down like this, even if Layout and Sidebar don’t use user.:
<App user={user}>
<Layout user={user}>
<Sidebar user={user}>
<UserProfile user={user} />
</Sidebar>
</Layout>
</App>This is called Prop Drilling: passing the same data through many layers of components that don’t actually need it.
Why is this bad?
- Components receive props they don’t use
- Code becomes hard to maintain
- Deep trees become messy
- Refactoring becomes painful
Context
Context allows you to share data globally without passing props manually at every level.
It’s built-in state management for small-to-medium React apps.
To use Context, you need three specific parts: Create the Context, Provide the Context, and Consume the Context.
- Create the Context
This is the “container” for your data.
import { createContext } from "react";
// Create context
export const UserContext = createContext();This creates a data channel.
Get Nimmikrishnab’s stories in your inbox
Join Medium for free to get updates from this writer.
2. Provide the Context
Wrap the part of your component tree that needs the data in a Provider. The Provider makes data available to all components inside it.
import { UserContext } from "./UserContext";
function App() {
const user = { name: "Harry" };
return (
<UserContext.Provider value={user}>
<Layout />
</UserContext.Provider>
);
}3. Consume the context (useContext)
Inside a child component, use the useContext hook to grab the data.
import { useContext } from "react";
import { UserContext } from "./UserContext";
function UserProfile() {
const user = useContext(UserContext);
return <h2>{user.name}</h2>;
}
// No props passed through intermediate components.// without context
App → Layout → Sidebar → UserProfile
(props passed manually each level)
// with context
App (Provider)
↓
All nested components can access data directlyUpdating Context State
Context isn’t just for static data. You can pass an object containing both the state and the updater function into the value prop.
// In the Provider
const value = {
user,
logout: () => setUser(null)
};
return (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
);When to Use Context (and When Not To)
While Context is powerful, it shouldn’t be your default for everything.
- Good for: “Global” data like Themes (Dark/Light mode), User authentication, Language settings (i18n), or simple Shopping Carts.
- Bad for: Highly frequent updates (like a text input or a game loop). When a Context value changes, every component consuming that context re-renders, which can lead to performance lag.
Context vs. Redux/Zustand (popular state management libraries)
- Context API: Best for low-frequency updates and smaller apps. It’s built into React, so there is no extra bundle size.
- Redux/Zustand: Better for complex logic, high-performance needs, and “undo/redo” features. They offer better debugging tools (DevTools).
Summary
Context API lets you share state globally without manually passing props down the component tree.
- Create the context using
createContext(). - Wrap the parent in
<Context.Provider value={...}>. - Access the data in any child using
useContext(Context). - Use it sparingly to avoid unnecessary re-renders.








