← Back to React Course | Chapter 5: Core Hooks | Lesson 4 of 12

useContext Hook

useContext is a walkie-talkie that lets a deeply nested component hear a value being broadcast from way up the component tree, without anyone in between having to relay it.

Creating and Providing Context

React.createContext(defaultValue) creates a Context object. Wrapping part of your component tree in <MyContext.Provider value={...}> makes that value available to every component nested inside it, no matter how many levels deep.

Note: Create context objects outside of any component, at the module's top level, so they aren't recreated on every render.

Warning: A Provider without a value prop falls back silently to the context's default value, which can be confusing to debug.

Example: Creating and Providing Context

markup
<!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 App() {
      return (
        <ThemeContext.Provider value="dark">
          <Toolbar />
        </ThemeContext.Provider>
      );
    }
    function Toolbar() {
      const theme = React.useContext(ThemeContext);
      return <p>Current theme: {theme}</p>;
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>

Reading Context with useContext

Any component nested inside the matching Provider can call useContext(MyContext) to read the current value directly, without that value being passed down explicitly through every intermediate component's props.

Note: useContext works no matter how deeply the consuming component is nested -- there's no need to manually pass the value through every layer.

Warning: A component using useContext outside of any matching Provider will get the context's default value, not an error.

Example: Reading Context with useContext

markup
<!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 UserContext = React.createContext({ name: "Guest" });
    function DeeplyNestedGreeting() {
      const user = React.useContext(UserContext);
      return <p>Hello, {user.name}!</p>;
    }
    function App() {
      return (
        <UserContext.Provider value={{ name: "Sam" }}>
          <div><div><DeeplyNestedGreeting /></div></div>
        </UserContext.Provider>
      );
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>

Avoiding Prop Drilling

Without context, sharing a value with a deeply nested component means passing it as a prop through every component in between, even ones that don't use it themselves -- this is called 'prop drilling'. Context solves this by letting the deeply nested component read the value directly.

Note: Reach for context specifically when you notice you're passing the same prop through several layers of components that don't otherwise need it.

Warning: Using context for absolutely everything, even values only needed by one or two nearby components, adds unnecessary indirection -- plain props are often simpler.

Example: Avoiding Prop Drilling

markup
<!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 CountContext = React.createContext(0);
    function DeepDisplay() {
      const count = React.useContext(CountContext);
      return <p>Count from context: {count}</p>;
    }
    function App() {
      const [count, setCount] = React.useState(0);
      return (
        <CountContext.Provider value={count}>
          <button onClick={() => setCount(count + 1)}>Increment</button>
          <div><DeepDisplay /></div>
        </CountContext.Provider>
      );
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>
Common Mistakes
  1. Forgetting to wrap the components that need the value in the matching Context.Provider.
  2. Creating a new context but never providing a value, leaving consumers stuck with only the default value.
  3. Overusing context for values that change very frequently, causing every consumer to re-render often.
Chapter Summary
  • Context lets you share a value across many components without passing props manually at every level.
  • createContext creates a Context object; .Provider supplies a value; useContext reads it.
  • Any descendant component can read the context value with useContext, no matter how deeply nested.
  • Context is ideal for global-ish data like themes, logged-in user info, or language settings.
Browser Support

createContext and useContext are available since React 16.3 and React 16.8 respectively.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.