To update context values from a child component, keep the state in the provider and pass both the value and its setter through context. A child reads the pair with useContext and calls the setter to change the value for the whole subtree. This is the standard pattern for shared values like a theme or the signed-in user, and it works from any depth.
Where the state lives
A context object has no built-in way to change its default, and the default never updates. To make the value dynamic, the provider must own state and pass the current value on every render.
import { createContext, useState } from "react";
export const ThemeContext = createContext(null);
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
return (
<ThemeContext value={{ theme, setTheme }}>
{children}
</ThemeContext>
);
}ThemeProvider owns the theme state and passes an object with both the current theme and setTheme. Extracting the provider into its own component keeps the state wiring in one place.
Read and update from a child
A child reads the same object and calls setTheme inside an event handler. The update follows the normal state rules, so the provider re-renders and pushes a new value down.
import { useContext } from "react";
import { ThemeContext } from "./theme-context";
function Settings() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<label>
<input type="checkbox" checked={theme === "dark"} onChange={(e) => setTheme(e.target.checked ? "dark" : "light")} />
Dark mode
</label>
);
}Checking the box calls setTheme with dark. The provider re-renders with the new value, and every component that reads ThemeContext re-renders too.
Wire it up at the top
Wrap the part of the app that needs the value in the provider. Components outside the provider keep their own defaults.
import { ThemeProvider } from "./theme-context";
export default function App() {
return (
<ThemeProvider>
<Settings />
</ThemeProvider>
);
}Settings appears inside the provider, so it can read and update the theme. Any component added later inside ThemeProvider gets the same value without new props.
Why pass the setter through context
A child can only update the value if it receives the setter. Passing just theme would let components read the value but never change it.
Including setTheme in the same object gives every reader the option to update, which suits shared controls like a theme toggle. If only one component updates the value, keep the setter local and pass it down as a prop instead of publishing it to the whole tree.
Update from any depth
Context passes through intermediate components, so a reader can update the value from any depth. A button inside a modal, a sidebar, and a footer can all call the same setTheme without props connecting them. The only requirement is that each reader sits inside the provider.
What not to do: mutate the value
The provider creates a new object on each render. Mutating that object from a child would not update state and would hide the change from React. Always call the setter so React schedules a new render with a fresh value.
What happens on update
When the value changes, React re-renders every component that reads that context, starting from the provider that received the new value. React compares the previous and next values with Object.is, so the provider must pass a new object to signal a change. Components that do not read the context can still re-render as part of their own parents.
What to learn next
The provider in this article returns a null default, so the object is only safe inside ThemeProvider. See safe default values for React context for the guard that catches a missing provider. For the complete create, provide, read flow, see the React context tutorial.
Rune AI
Key Insights
- A context object has no setter; the provider owns the state.
- Pass an object containing the value and its setter.
- Children read the pair with useContext and call the setter.
- Every reader re-renders when the value changes.
- Extract the provider into a component to keep the tree tidy.
Frequently Asked Questions
Why not update the context object directly?
Does every child re-render when the value changes?
Should the setter be passed through context?
Conclusion
To update a context value from a child, hold the state in the provider and pass both the value and its setter through context. Children read the pair with useContext and call the setter to change the value for the whole subtree.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.