React Context: When to Use It
Context is powerful but often misused. Here's when it makes sense and when it doesn't.
What Context Solves
Prop drilling. Passing data through many component levels:
// Without context - prop drilling
<App>
<Layout user={user}>
<Header user={user}>
<UserMenu user={user} />
</Header>
</Layout>
</App>
Every component in the chain needs to pass `user` even if they don't use it.
Context Solution
const UserContext = createContext(null);
function App() {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
<Layout>
<Header />
</Layout>
</UserContext.Provider>
);
}
function UserMenu() {
const { user } = useContext(UserContext);
return <span>{user?.name}</span>;
}
Now `UserMenu` accesses user directly. No drilling.
Good Use Cases
Theme
const ThemeContext = createContext('light');
function Button() {
const theme = useContext(ThemeContext);
return <button className={theme}>Click</button>;
}
Current User
function useCurrentUser() {
return useContext(UserContext);
}
Locale/i18n
const { locale, t } = useContext(LocaleContext);
Feature Flags
const flags = useContext(FeatureFlagContext);
Bad Use Cases
Frequently Changing Data
Context re-renders all consumers when value changes.
// ❌ Bad - updates every second, all consumers re-render
<Context.Provider value={{ time: Date.now() }}>
Component-Local State
If state only matters to one component tree, just use local state.
// ❌ Overkill
<ModalOpenContext.Provider value={isOpen}>
// ✅ Just lift state to common parent
function Page() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Button onClick={() => setIsOpen(true)} />
{isOpen && <Modal onClose={() => setIsOpen(false)} />}
</>
);
}
Complex State Logic
For complex state with many actions, consider useReducer or a state library.
Performance Tip
Split contexts to avoid unnecessary re-renders:
// ❌ One big context
const AppContext = createContext({ user, theme, locale, notifications });
// ✅ Separate contexts
const UserContext = createContext(null);
const ThemeContext = createContext('light');
const LocaleContext = createContext('en');
Now changing theme doesn't re-render components that only use user.
The Rule
Use context for:
- Data that many components need
- Data that rarely changes
- "Global" concerns (theme, user, locale)
Don't use context for:
- Frequently updating data
- Data used by only a few nearby components
- Complex state management