Context with useContext
Context lets you pass data through the component tree without manually passing props.
Creating Context
import { createContext, useContext } from "react";
const ThemeContext = createContext("light");
Providing Context
Wrap components with the Provider:
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
Consuming with useContext
function Button() {
const theme = useContext(ThemeContext);
return <button className={theme}>Click</button>;
}
useContext finds the nearest Provider up the tree.
Avoiding Prop Drilling
Without context, passing theme through every level:
// Bad — prop drilling
<App theme="dark">
<Layout theme="dark">
<Sidebar theme="dark">
<Button theme="dark" />
</Sidebar>
</Layout>
</App>
// Good — context
<App>
<Layout>
<Sidebar>
<Button /> {/* reads theme directly */}
</Sidebar>
</Layout>
</App>
Dynamic Context
Combine with useState for dynamic values:
function App() {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Main />
</ThemeContext.Provider>
);
}
Best Practices
- Use context for global data (theme, auth, locale)
- Don’t use context for high-frequency updates
- Split large contexts into smaller ones
Common Mistakes
- Passing new objects/values on every render defeats memoization
- Using context for every prop — use props for local data
- Not wrapping with Provider causes default values to be used