React Context API
The React Context API provides a way to pass data through the component tree without having to pass props down manually at every level (a problem known as "prop drilling").
Why use Context?
In a typical React application, data is passed top-down (parent to child) via props. But this can be cumbersome for certain types of props (e.g., locale preference, UI theme, authenticated user) that are required by many components within an application. Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.
Creating and Providing Context
To use Context, you first need to create it using React.createContext().
import React, { createContext, useState } from 'react';
// 1. Create the Context
export const ThemeContext = createContext();
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
};
// 2. Provide the Context value to children
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
Consuming Context
The modern way to consume Context in functional components is by using the useContext hook.
import React, { useContext } from 'react';
import { ThemeContext } from './ThemeProvider';
export default function ThemedButton() {
// 3. Consume the Context
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
onClick={toggleTheme}
style={{
backgroundColor: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#000' : '#fff'
}}
>
Toggle to {theme === 'light' ? 'Dark' : 'Light'} Mode
</button>
);
}
Performance Considerations
While Context is powerful, it shouldn't be used for all state.
The Re-render Issue: Whenever the value provided to <Context.Provider> changes, every component that consumes that context will re-render. If you put your entire application state into a single Context, a tiny change will cause your entire app to re-render.
Best Practices
- Split Contexts Logically: Instead of one giant
AppContext, haveThemeContext,AuthContext, etc. - Memoize Context Values: If your context value is an object, wrap it in
useMemoso that the object reference doesn't change on every render unless its underlying data changes.
const contextValue = useMemo(() => ({ theme, toggleTheme }), [theme]);
return (
<ThemeContext.Provider value={contextValue}>
{children}
</ThemeContext.Provider>
);
When to Use Context
Use Context for:
- Theming (dark/light mode)
- Current authenticated user data
- Language/localization preferences
- Global UI state (e.g., is the sidebar open?)
Do not use Context for:
- High-frequency state changes (e.g., dragging, animations, typing in a text input).
- Complex relational state (consider Redux or Zustand instead).
- Data fetching and caching (consider React Query or SWR instead).