How to Update Context Values from Child Components

Keep context state in a provider component and pass the value with its setter, so child components can read and update shared state.

6 min read

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.

App.jsxApp.jsx
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.

App.jsxApp.jsx
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.

App.jsxApp.jsx
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

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.
RunePowered by Rune AI

Frequently Asked Questions

Why not update the context object directly?

The context object only identifies shared data. It has no setter. The provider must own state and pass a new value on the next render.

Does every child re-render when the value changes?

Yes. Every component that reads the context with useContext re-renders when the provider receives a different value.

Should the setter be passed through context?

Only when children must update the shared value. If children only read it, pass the value alone.

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.