React Context Tutorial: Create, Provide, and Read Context

Create a context with createContext, provide a value through a provider, and read it with useContext to share data across a React component tree.

6 min read

React context lets a component share data with the entire tree below it without passing props through every intermediate level. You create a context object, provide a value from a parent, and read that value anywhere below with the useContext Hook. A theme setting is the classic example because it shows all three steps in a few lines, and the same steps work for any shared value, from a color scheme to the signed-in user.

Step 1: Create the context

A context starts as an object returned by createContext. Call it at module scope, outside any component, and export it so other files import the same object. The argument is a fallback value React uses when no provider sits above a reader.

App.jsxApp.jsx
import { createContext } from "react";
 
export const ThemeContext = createContext("light");

The fallback here is the string light. It is only a last resort, and it never changes on its own.

Step 2: Provide the value

Wrap a section of the tree with the context component and pass the current value through its value prop. Every component below that provider can now see the value. The value prop accepts any JavaScript value: a string, a number, an object, or even a function.

App.jsxApp.jsx
import { ThemeContext } from "./theme-context";
 
export default function App() {
  return (
    <ThemeContext value="dark">
      <Page />
    </ThemeContext>
  );
}

This App provides dark to everything inside Page. In React 19 you can render the context object directly as a provider. In earlier versions, write ThemeContext.Provider instead, with the same value prop.

Step 3: Read the value

Any component below the provider reads the value with the useContext Hook. It takes the context object and returns the value from the nearest provider above. The Hook returns the same value the provider passed, not a copy.

App.jsxApp.jsx
import { useContext } from "react";
import { ThemeContext } from "./theme-context";
 
function Button({ children }) {
  const theme = useContext(ThemeContext);
  return <button className={`btn-${theme}`}>{children}</button>;
}

This Button renders with the class btn-dark when the provider passes dark. It never receives theme through props, and no component between App and Button has to forward it.

A complete theme example

Combine the three steps in one file to see the full flow. The provider and the reader share one context object, which is what connects them.

App.jsxApp.jsx
import { createContext, useContext } from "react";
const ThemeContext = createContext("light");
 
function Button() {
  const theme = useContext(ThemeContext);
  return <button className={`btn-${theme}`}>Save</button>;
}
 
export default function App() {
  return <ThemeContext value="dark"><Button /></ThemeContext>;
}

The button appears with dark styling because the provider passes dark. Change the value prop to light and the button switches without touching the Button component.

Nested providers override the value

A provider only affects the tree below it, and the nearest provider wins for its own subtree. You can wrap one section in a different value while the rest of the app keeps the outer value.

App.jsxApp.jsx
function App() {
  return (
    <ThemeContext value="dark">
      <Page />
      <ThemeContext value="light">
        <Preview />
      </ThemeContext>
    </ThemeContext>
  );
}

Page and everything inside it read dark, while Preview reads light. This is how one part of the app can override a shared value without changing it for everyone else.

How updates flow

A context value becomes useful when it changes. If the provider passes a different value, React re-renders every component that reads that context with useContext. See how to update context values from child components to wire the value to state.

When context is the right tool

Context shines when several distant components need the same data, such as a theme, the signed-in user, or a locale. It is not a universal replacement for props.

A prop passed through one or two levels stays clearer than an implicit context. See prop drilling and how to fix it for the cases where context earns its keep.

What context is not

Context is not a state management library and not a global store. It only passes a value down the tree, and that value disappears when the provider unmounts. Treat it as a pipe for shared data, and reach for a reducer or a store when the state logic outgrows a single value.

What to learn next

The default value you pass to createContext matters more than it looks. Continue with safe default values for React context.

Rune AI

Rune AI

Key Insights

  • createContext creates a context object that identifies shared data.
  • A provider sets the value for every component below it.
  • useContext reads the value from the nearest provider above.
  • The default value is only a fallback when no provider exists.
  • When the value changes, every reader re-renders.
RunePowered by Rune AI

Frequently Asked Questions

What does createContext return?

It returns a context object. The object itself does not hold data. It only identifies which context a component provides or reads.

Do I have to export the context object?

Usually yes. Components in other files provide and read the same context, so exporting it from one module keeps the object identity consistent.

What happens when a component reads context with no provider above?

useContext returns the default value passed to createContext. That fallback is static and never changes.

Conclusion

React context shares data down a tree in three steps: create a context with createContext, provide a value from a parent, and read it with useContext. Use it when several distant components need the same data, and keep props for data that moves through a short, explicit path.