Context Provider Pattern
In this page:
const NameContext = createContext(defaultValue);
function NameProvider({ children }) {
const [value, setValue] = useState(initial);
return (
<NameContext.Provider value={{ value, setValue }}>
{children}
</NameContext.Provider>
);
}
const useName = () => useContext(NameContext);
Creating and Providing Context
React.createContext() creates a Context object with a default value. Wrapping part of the component tree in <MyContext.Provider value={...}> makes that value available to every component nested inside it, no matter how deep, without threading it through props manually.
Example: Creating and Providing Context
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const ThemeContext = React.createContext("light");
function Display() {
const theme = React.useContext(ThemeContext);
return <p>Current theme: {theme}</p>;
}
function App() {
return (
<ThemeContext.Provider value="dark">
<Display />
</ThemeContext.Provider>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Wrapping Provider + State Together
A common pattern is building a dedicated Provider component that holds the state itself and passes it (plus a setter) down through context, rather than sprinkling useState calls across the app.
This keeps all the related state and update logic in one place.
Example: Wrapping Provider + State Together
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const ThemeContext = React.createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = React.useState("light");
return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>;
}
function ThemeToggle() {
const { theme, setTheme } = React.useContext(ThemeContext);
return <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>Theme: {theme}</button>;
}
function App() {
return <ThemeProvider><ThemeToggle /></ThemeProvider>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Wrapping useContext in a Custom Hook
Instead of every consumer calling useContext(ThemeContext) directly, it's common to expose a small custom hook like useTheme() that does this internally and can also throw a helpful error if used outside the provider.
This makes misuse easy to catch early.
Example: Wrapping useContext in a Custom Hook
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const ThemeContext = React.createContext(null);
function ThemeProvider({ children }) {
const [theme, setTheme] = React.useState("light");
return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>;
}
function useTheme() {
const ctx = React.useContext(ThemeContext);
if (!ctx) throw new Error("useTheme must be used within a ThemeProvider");
return ctx;
}
function ThemeLabel() {
return <p>Theme is: {useTheme().theme}</p>;
}
function App() {
return <ThemeProvider><ThemeLabel /></ThemeProvider>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Passing a brand-new object literal as the Context value on every render, causing every consumer to re-render even when the actual data didn't change.
- Forgetting to wrap the part of the tree that needs the context in the Provider, so useContext returns the default (often undefined) value.
- Putting everything into one giant global context instead of splitting concerns into focused, separate contexts.
- The provider pattern wraps a piece of the component tree in a Context.Provider to make a value available to all its descendants.
- Consumers read the value with useContext, without needing it passed down manually through every level of props.
- Wrapping the provider's value in useMemo avoids creating a new object on every render, preventing unnecessary consumer re-renders.
- It's common to pair a provider with a custom hook (like useTheme()) that wraps useContext for a cleaner consumer API.
Available since React 16.3 (stable Context API).
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: