Context
Data usually flows from parent to child through props. But if a value is needed five levels down, it has to be passed through every component in between — this is called prop drilling. Context solves it differently: the value becomes available to any component inside the wrapper, at any depth.
Creating a context
Let us add a light and dark theme switch to the blog. A context is created once in its own file — src/ThemeContext.js:
// src/ThemeContext.js
import { createContext } from 'react';
export const ThemeContext = createContext('light');The argument of createContext is the default value. A component with no wrapper above it gets that value.
Providing a value
The context itself serves as the wrapper component. The value is set with the value prop, usually in src/App.jsx:
// src/App.jsx
import { ThemeContext } from './ThemeContext.js';
import PostList from './components/PostList.jsx';
export default function App() {
return (
<ThemeContext value="dark">
<PostList />
</ThemeContext>
);
}In React 19 the context is written as a tag directly. In older versions the wrapper was ThemeContext.Provider — such code is still around and still works.
Reading the value
Any component inside reads the value with the useContext hook. Create src/components/ThemedBox.jsx:
// src/components/ThemedBox.jsx
import { useContext } from 'react';
import { ThemeContext } from '../ThemeContext.js';
export default function ThemedBox({ children }) {
const theme = useContext(ThemeContext);
return <div className={`box box-${theme}`}>{children}</div>;
}If a post card somewhere inside PostList is wrapped in ThemedBox, it gets the class box-dark. The components in between do not need to know about the theme.
Context with state
To make the theme switchable, put state into the context together with its setter:
// src/App.jsx
import { useState } from 'react';
export default function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext value={{ theme, setTheme }}>
<ThemeSwitch />
<PostList />
</ThemeContext>
);
}// src/components/ThemeSwitch.jsx
import { useContext } from 'react';
import { ThemeContext } from '../ThemeContext.js';
export default function ThemeSwitch() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Theme: {theme}
</button>
);
}The button switches the theme, and every component reading the context re-renders with the new value. useContext now returns an object, so in ThemedBox write const { theme } = useContext(ThemeContext).
When to use it
Context suits data needed in many places: the theme, the interface language, the current user. For data of one or two components, props are enough — with them it is easier to see where a value came from.
Common mistake
A component that reads a context outside its wrapper silently gets the default value:
export default function App() {
return (
<>
<ThemeContext value="dark">
<PostList />
</ThemeContext>
<Footer /> {/* the theme here is 'light' */}
</>
);
}Make sure the wrapper covers every component that needs the value. It is often placed at the very top — in src/main.jsx or at the root of App.