React Hooks Context API No Prop Drilling
The useContext() Hook provides a simple and elegant way to read values from React’s Context API — without manually passing props through multiple component levels.
Context is designed to share global data such as user info, app settings, or theme state across components, helping you completely avoid “prop drilling.”
Without context, data often has to be passed through several intermediate components that don’t actually use it, making code cluttered and harder to maintain.
// Example of prop drilling without Context
function App() {
const user = "John";
return <Parent user={user} />;
}
function Parent({ user }) {
return <Child user={user} />;
}
function Child({ user }) {
return <p>Hello, {user}!</p>;
}
The user value is needed only in the Child component, but it still has to be passed through Parent. As your component tree grows deeper, this pattern becomes painful and error-prone.
With useContext(), you can access shared values directly from the nearest context provider, eliminating the need for prop drilling.
// Using Context and useContext to avoid prop drilling
import React, { createContext, useContext } from "react";
// 1️⃣ Create Context
const UserContext = createContext();
// 2️⃣ Create Provider Component
function App() {
return (
<UserContext.Provider value="John">
<Dashboard />
</UserContext.Provider>
);
}
// 3️⃣ Consume Context in Child Component
function Dashboard() {
const user = useContext(UserContext);
return <h5 className="text-center">Welcome, {user}!</h5>;
}
export default App;
Here, Dashboard directly reads the value from UserContext using useContext(UserContext). Intermediate components no longer need to pass the user as a prop.
Step 1: Create a Context
// Creating a context with an optional default value
const MyContext = createContext(defaultValue);
Step 2: Wrap components with a Provider
// Providing a value to all nested components
function App() {
const value = "Some shared value";
return (
<MyContext.Provider value={value}>
<ChildComponent />
</MyContext.Provider>
);
}
Step 3: Consume context with useContext()
// Reading the nearest MyContext value
function ChildComponent() {
const value = useContext(MyContext);
return <p>Value from context: {value}</p>;
}
Let’s see how Context can share theme values (light/dark) and a toggle function across multiple components.
// Sharing theme and toggleTheme using Context + useContext
import React, { createContext, useContext, useState } from "react";
// Create Context
const ThemeContext = createContext();
// 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>
);
}
// Component consuming context
function ThemedBox() {
const { theme, toggleTheme } = useContext(ThemeContext);
const styles = {
backgroundColor: theme === "light" ? "#f8f9fa" : "#212529",
color: theme === "light" ? "#212529" : "#f8f9fa",
padding: "20px",
borderRadius: "8px",
textAlign: "center"
};
return (
<div style={styles}>
<p>Current Theme: {theme}</p>
<button className="btn btn-outline-info" onClick={toggleTheme}>
Toggle Theme
</button>
</div>
);
}
// Root Component
function App() {
return (
<ThemeProvider>
<ThemedBox />
</ThemeProvider>
);
}
export default App;
The ThemeContext provides both the current theme and a toggleTheme function to all components wrapped by ThemeProvider. Any component inside can switch the theme without needing props.
You can safely combine multiple contexts to share different types of global data (e.g., auth + language).
// Using AuthContext and LangContext together
const AuthContext = createContext();
const LangContext = createContext();
function App() {
return (
<AuthContext.Provider value="John">
<LangContext.Provider value="English">
<Profile />
</LangContext.Provider>
</AuthContext.Provider>
);
}
function Profile() {
const user = useContext(AuthContext);
const lang = useContext(LangContext);
return <p>{user} is viewing this page in {lang}.</p>;
}
Each useContext() call reads from a different provider. This lets you keep concerns separated (authentication, theme, language, etc.) while still avoiding prop drilling.
| Aspect | Prop Drilling | useContext() |
|---|---|---|
| Data Sharing | Passed manually through each level | Available via nearest Provider |
| Ease of Maintenance | Harder as the tree grows | Simpler and more scalable |
| Performance | Good for isolated props | Context updates re-render all consumers |
ThemeContext, UserContext, or AuthContext.useTheme(), useAuth()) for cleaner code.UserContext to store and display user info across multiple components.LanguageContext that switches between English and Hindi text.AuthContext + ThemeContext).useTheme() or useAuth().Goal: Understand how useContext() offers a simple and powerful way to share global data across React components without tedious prop drilling.