Documentation Index
Fetch the complete documentation index at: https://mintlify.com/facebook/react/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Context provides a way to pass data through the component tree without having to pass props down manually at every level. It’s designed to share data that can be considered “global” for a tree of React components.
Creating Context
Use createContext to create a context object. The context comes with a Provider and Consumer.
import { createContext } from 'react';
const ThemeContext = createContext('light');
API Signature
From packages/react/src/ReactContext.js:14:
function createContext<T>(defaultValue: T): ReactContext<T>
The function returns a context object with the following structure:
{
$$typeof: REACT_CONTEXT_TYPE,
Provider: Context, // Used to provide values
Consumer: ConsumerObject, // Used to consume values (legacy)
_currentValue: defaultValue,
_currentValue2: defaultValue,
_threadCount: 0
}
Using Context with Provider
The Provider component allows consuming components to subscribe to context changes.
import { createContext } from 'react';
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
Consuming Context
With useContext Hook (Recommended)
The useContext hook is the modern way to consume context values.
import { useContext } from 'react';
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button className={theme}>Click me</button>;
}
From packages/react/src/ReactHooks.js:56, calling useContext(Context.Consumer) is not supported:if (Context.$$typeof === REACT_CONSUMER_TYPE) {
console.error(
'Calling useContext(Context.Consumer) is not supported and will cause bugs. ' +
'Did you mean to call useContext(Context) instead?'
);
}
Always pass the Context object directly, not Context.Consumer.
With Context.Consumer (Legacy)
The Consumer component uses a render prop pattern:
function ThemedButton() {
return (
<ThemeContext.Consumer>
{theme => (
<button className={theme}>
Click me
</button>
)}
</ThemeContext.Consumer>
);
}
Practical Examples
Theme Context
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext({
theme: 'light',
toggleTheme: () => {}
});
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
function ThemedComponent() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<div className={`theme-${theme}`}>
<h1>Current theme: {theme}</h1>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}
function App() {
return (
<ThemeProvider>
<ThemedComponent />
</ThemeProvider>
);
}
Authentication Context
import { createContext, useContext, useState } from 'react';
const AuthContext = createContext(null);
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = async (username, password) => {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ username, password })
});
const userData = await response.json();
setUser(userData);
};
const logout = () => {
setUser(null);
};
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}
function LoginForm() {
const { login } = useAuth();
const handleSubmit = (e) => {
e.preventDefault();
const formData = new FormData(e.target);
login(formData.get('username'), formData.get('password'));
};
return (
<form onSubmit={handleSubmit}>
<input name="username" type="text" placeholder="Username" />
<input name="password" type="password" placeholder="Password" />
<button type="submit">Login</button>
</form>
);
}
function UserProfile() {
const { user, logout } = useAuth();
if (!user) {
return <LoginForm />;
}
return (
<div>
<h2>Welcome, {user.name}!</h2>
<button onClick={logout}>Logout</button>
</div>
);
}
Multiple Contexts
You can use multiple contexts in a single component:
import { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
const UserContext = createContext(null);
const LanguageContext = createContext('en');
function App() {
return (
<ThemeContext.Provider value="dark">
<UserContext.Provider value={{ name: 'Alice' }}>
<LanguageContext.Provider value="es">
<Dashboard />
</LanguageContext.Provider>
</UserContext.Provider>
</ThemeContext.Provider>
);
}
function Dashboard() {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
const language = useContext(LanguageContext);
return (
<div className={`theme-${theme} lang-${language}`}>
<h1>Welcome, {user.name}!</h1>
</div>
);
}
Default Values
The default value is used when a component doesn’t have a matching Provider above it:
const ThemeContext = createContext('light');
function ThemedButton() {
// If no Provider exists, 'light' will be used
const theme = useContext(ThemeContext);
return <button className={theme}>Click me</button>;
}
function App() {
// No Provider wrapping ThemedButton
// It will use the default value 'light'
return <ThemedButton />;
}
Updating Context Values
Context value updates trigger re-renders of all consuming components. For performance-critical applications, consider:
- Splitting contexts by update frequency
- Using composition to limit re-renders
- Memoizing context values with
useMemo
import { createContext, useContext, useState, useMemo } from 'react';
const ConfigContext = createContext(null);
function ConfigProvider({ children }) {
const [config, setConfig] = useState({
theme: 'light',
language: 'en',
fontSize: 16
});
// Memoize the context value to prevent unnecessary re-renders
const value = useMemo(() => ({
config,
updateConfig: (updates) => {
setConfig(prev => ({ ...prev, ...updates }));
}
}), [config]);
return (
<ConfigContext.Provider value={value}>
{children}
</ConfigContext.Provider>
);
}
Context Internal Implementation
From packages/react/src/ReactContext.js:18:
const context: ReactContext<T> = {
$$typeof: REACT_CONTEXT_TYPE,
// Support for concurrent renderers
_currentValue: defaultValue,
_currentValue2: defaultValue,
_threadCount: 0,
Provider: context,
Consumer: {
$$typeof: REACT_CONSUMER_TYPE,
_context: context,
},
};
React maintains separate _currentValue and _currentValue2 fields to support multiple concurrent renderers (e.g., React DOM and React Native running simultaneously).
Best Practices
-
Create custom hooks: Wrap
useContext in custom hooks for better error handling and type safety
-
Split contexts: Don’t put all global state in one context. Split by domain or update frequency
-
Provide meaningful defaults: Default values should be valid states, not just
null
-
Memoize context values: Use
useMemo to prevent unnecessary re-renders
-
Document context shape: Use TypeScript or JSDoc to document the context structure
When to Use Context
Context is ideal for:
- Theme data (color, font, etc.)
- Current authenticated user
- Locale/language preferences
- UI state (modals, tooltips)
Context may not be ideal for:
- Frequently changing data (use state management libraries)
- Data that only a few components need (use props)
- Performance-critical updates (consider alternatives)
See Also
- Hooks - Learn about
useContext and other hooks
- Component API - Component lifecycle and APIs