Back to all notes

use() with Context : Conditional Context Reads in React 19

Read context with the new use() hook, even inside conditions and loops.

1 min read

useContext has one rule that has bitten me plenty: it must be called unconditionally, at the top level. React 19’s use() relaxes that. You pass it the context object itself, and it works inside conditions and loops.

The top-level read

With useContext, the theme gets read before any branching, and providers use the .Provider syntax:

import { createContext, useContext } from "react";

const ThemeContext = createContext(null);

function Button({ show, children }) {
  const theme = useContext(ThemeContext); // must be top-level
  if (!show) return null;
  return <button className={"button-" + theme}>{children}</button>;
}

function App() {
  return (
    <div>
      <h1>Use Hook with Context</h1>
      <ThemeContext.Provider value="dark">
        <Button show={true}>Sign up</Button>
        <Button show={false}>Log in</Button>
      </ThemeContext.Provider>
    </div>
  );
}

export default App;

That comment is the whole problem. The read runs even when show is false and the button never renders.

Read it inside the branch

use() puts the read where the value is actually needed:

import { createContext, use } from "react";

const ThemeContext = createContext(null);

function Button({ show, children }) {
  if (show) {
    const theme = use(ThemeContext);
    const className = "button-" + theme;
    return <button className={className}>{children}</button>;
  }
  return null;
}

function App() {
  return (
    <div>
      <h1>Use Hook with Context</h1>
      <ThemeContext.Provider value="dark">
        <Button show={true}>Sign up</Button>
        <Button show={false}>Log in</Button>
      </ThemeContext.Provider>
    </div>
  );
}

export default App;

use(ThemeContext) only runs in the branch that renders. On the providing side, you can provide with the context object directly: <ThemeContext value="...">.

Keep the context values themselves stable rather than recreating the objects every render. I put theme and locale in coarser contexts and keep dynamic data in finer ones. And check that your tooling and runtime support React 19’s use() before you commit to it.