~/hackweb.dev
Context with useContext
Quiz
...

Context with useContext

intermediate · updated Tue Sep 08 2026Contribute

Share data across components without prop drilling using Context.

STEP 1 · Context vs Prop Drilling

Context vs Prop DrillingStep 1 / 7
theme = "dark"Context.Provider<App /><Layout /><Sidebar /><Button />

A deeply nested <Button /> needs the current theme — one value, four levels down.

Context with useContext

Context lets you pass data through the component tree without manually passing props.

Creating Context

import { createContext, useContext } from "react";

const ThemeContext = createContext("light");

Providing Context

Wrap components with the Provider:

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

Consuming with useContext

function Button() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>Click</button>;
}

useContext finds the nearest Provider up the tree.

Avoiding Prop Drilling

Without context, passing theme through every level:

// Bad — prop drilling
<App theme="dark">
  <Layout theme="dark">
    <Sidebar theme="dark">
      <Button theme="dark" />
    </Sidebar>
  </Layout>
</App>

// Good — context
<App>
  <Layout>
    <Sidebar>
      <Button /> {/* reads theme directly */}
    </Sidebar>
  </Layout>
</App>

Dynamic Context

Combine with useState for dynamic values:

function App() {
  const [theme, setTheme] = useState("light");

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <Main />
    </ThemeContext.Provider>
  );
}

Best Practices

  1. Use context for global data (theme, auth, locale)
  2. Don’t use context for high-frequency updates
  3. Split large contexts into smaller ones

Common Mistakes

  1. Passing new objects/values on every render defeats memoization
  2. Using context for every prop — use props for local data
  3. Not wrapping with Provider causes default values to be used