🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
REFERENCEreact

react Documentation

LOADING ENGINE...

useContext

AI & DATA SCIENCE // usecontext

useContext lets a component read a value from the nearest matching Context Provider above it in the tree, without needing that value passed down manually through every intermediate component's props.

Syntax

const value = useContext(MyContext);

Deep Dive Course

Context solves prop drilling, the tedium of passing a value down through several layers of components that don't actually use it themselves, just to reach a deeply nested component that does — a Context.Provider higher up the tree supplies a value, and any descendant component can read it directly with useContext(MyContext), regardless of how many intermediate components sit in between, without those intermediate components needing to know or forward that value at all. A component using useContext automatically re-renders whenever the Provider's value changes, making context suitable for genuinely shared, cross-cutting data like a theme, authenticated user, or locale setting.

1Understanding useContext

Context solves prop drilling, the tedium of passing a value down through several layers of components that don't actually use it themselves, just to reach a deeply nested component that does — a Context.Provider higher up the tree supplies a value, and any descendant component can read it directly with useContext(MyContext), regardless of how many intermediate components sit in between, without those intermediate components needing to know or forward that value at all. A component using useContext automatically re-renders whenever the Provider's value changes, making context suitable for genuinely shared, cross-cutting data like a theme, authenticated user, or locale setting.

💡

Every component consuming a context with useContext re-renders whenever that context's value changes, even if the component only cares about part of a large value object — for state that changes frequently, consider splitting it into more granular contexts to avoid triggering unnecessary re-renders across unrelated consumers.

editor.html
import { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

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

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedButton />
    </ThemeContext.Provider>
  );
}
localhost:3000

2Practical Example

Here is a real-world application of useContext showing how it is used in production React code.

editor.html
import { createContext, useContext, useState } from 'react';

const UserContext = createContext(null);

function Profile() {
  const user = useContext(UserContext);
  return <p>Logged in as: {user?.name ?? 'Guest'}</p>;
}

function App() {
  const [user] = useState({ name: 'Ana' });
  return (
    <UserContext.Provider value={user}>
      <Profile />
    </UserContext.Provider>
  );
}
localhost:3000

3Best Practices

Follow these guidelines when working with useContext:

1. Use context for genuinely global or cross-cutting values, like theme, authenticated user, or locale, not as a blanket replacement for regular props between closely related components

2. Split frequently-changing state into its own, more granular context rather than bundling it with rarely-changing values, to avoid triggering unnecessary re-renders in components that only need the stable part

3. Provide a sensible default value when creating the context with createContext(), so components still behave reasonably even if used outside of an explicit Provider

⚠️

Tip: Every component consuming a context with useContext re-renders whenever that context's value changes, even if the component only cares about part of a large value object — for state that changes frequently, consider splitting it into more granular contexts to avoid triggering unnecessary re-renders across unrelated consumers.

editor.html
import { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

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

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedButton />
    </ThemeContext.Provider>
  );
}
localhost:3000

4Memoize the Provider's Value

An object literal passed as a Provider's value prop is a brand-new reference every time the Provider's parent re-renders, so every consumer of that context re-renders too — even when none of the actual data changed. Wrapping the value in useMemo keeps the same reference across renders unless its own dependencies change, preventing that re-render storm.

editor.html
// New object every render -> all consumers re-render
<Ctx.Provider value={{ user, theme }}>

// Stable reference -> only re-renders when user/theme change
const value = useMemo(() => ({ user, theme }), [user, theme]);
<Ctx.Provider value={value}>
localhost:3000

5Nested Providers: The Nearest One Wins

A consumer always reads from the closest matching Provider above it in the tree, not the outermost one. This lets an inner subtree locally override a context's value — like a dark-mode panel nested inside an otherwise light-themed app — without affecting any consumer outside that subtree.

editor.html
<ThemeContext.Provider value="light">
  <Sidebar /> {/* sees 'light' */}
  <ThemeContext.Provider value="dark">
    <Panel /> {/* sees 'dark' */}
  </ThemeContext.Provider>
</ThemeContext.Provider>
localhost:3000

Examples

Example 01Basic Usage
import { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

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

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedButton />
    </ThemeContext.Provider>
  );
}
Example 02Advanced Example
import { createContext, useContext, useState } from 'react';

const UserContext = createContext(null);

function Profile() {
  const user = useContext(UserContext);
  return <p>Logged in as: {user?.name ?? 'Guest'}</p>;
}

function App() {
  const [user] = useState({ name: 'Ana' });
  return (
    <UserContext.Provider value={user}>
      <Profile />
    </UserContext.Provider>
  );
}
Example 03Combining useContext with useReducer
const CartContext = createContext(null);

function cartReducer(state, action) {
  if (action.type === 'add') return [...state, action.item];
  return state;
}

function CartProvider({ children }) {
  const [items, dispatch] = useReducer(cartReducer, []);
  const value = useMemo(() => ({ items, dispatch }), [items]);
  return <CartContext.Provider value={value}>{children}</CartContext.Provider>;
}

Best Practices

  • Use context for genuinely global or cross-cutting values, like theme, authenticated user, or locale, not as a blanket replacement for regular props between closely related components
  • Split frequently-changing state into its own, more granular context rather than bundling it with rarely-changing values, to avoid triggering unnecessary re-renders in components that only need the stable part
  • Provide a sensible default value when creating the context with createContext(), so components still behave reasonably even if used outside of an explicit Provider
  • Wrap an object or array passed as a Provider's value prop in useMemo, so its reference stays stable across renders where the underlying data hasn't actually changed
  • Remember that a consumer reads from its nearest Provider, so a nested Provider can intentionally override a context's value for just its own subtree

Interview Question

Why does every component consuming a given context re-render whenever that context's value changes, even components that only actually use part of a larger context value object?

Hint: Think about what information React actually has available to determine which parts of a context's value a particular consuming component cares about.

useContext simply subscribes a component to the entire value currently provided by the nearest matching Provider, and React's context mechanism has no built-in way to know which specific properties of that value object a particular consuming component actually reads inside its own function body versus which ones it ignores entirely — from React's perspective, the value as a whole either changed or it didn't, since context comparison happens at the level of that one value, not at the level of individual properties within it. This means any component calling useContext on that context re-renders whenever the Provider passes a new value, even if the specific properties that particular component destructures out of it happened to stay the same, which is precisely why bundling frequently-changing and rarely-changing data together into one single large context value can cause components only interested in the stable part to re-render far more often than necessary — splitting such state into separate, more granular contexts is the standard way to avoid that.

Exercises

MediumPractice using useContext in a real scenario.
View Solution
import { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

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

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedButton />
    </ThemeContext.Provider>
  );
}
MediumThis Provider re-creates its value object on every render of App, causing every consumer to re-render unnecessarily. Fix it with useMemo.
View Solution
import { createContext, useMemo, useState } from 'react';

const SettingsContext = createContext(null);

function App() {
  const [lang, setLang] = useState('en');
  const value = useMemo(() => ({ lang, setLang }), [lang]);
  return (
    <SettingsContext.Provider value={value}>
      {/* children */}
    </SettingsContext.Provider>
  );
}

Frequently Asked Questions

Why does every component consuming a given context re-render whenever that context's value changes, even components that only actually use part of a larger context value object?

useContext simply subscribes a component to the entire value currently provided by the nearest matching Provider, and React's context mechanism has no built-in way to know which specific properties of that value object a particular consuming component actually reads inside its own function body versus which ones it ignores entirely — from React's perspective, the value as a whole either changed or it didn't, since context comparison happens at the level of that one value, not at the level of individual properties within it. This means any component calling useContext on that context re-renders whenever the Provider passes a new value, even if the specific properties that particular component destructures out of it happened to stay the same, which is precisely why bundling frequently-changing and rarely-changing data together into one single large context value can cause components only interested in the stable part to re-render far more often than necessary — splitting such state into separate, more granular contexts is the standard way to avoid that.

Why does a Provider's value prop need to be memoized with useMemo?

Every consumer of a context re-renders whenever the value React sees at that context's Provider is a different reference from before — and an object or array literal written directly in JSX, like value={{ user, theme }}, is recreated as a brand-new object on every single render of the Provider's parent component, regardless of whether user or theme actually changed. Without memoization, that means every consumer re-renders on every parent render, defeating much of the point of using context for performance-sensitive data. Wrapping the value in useMemo(() => ({ user, theme }), [user, theme]) makes React reuse the same object reference across renders where user and theme haven't changed, so consumers only re-render when the data they actually depend on changes.

Related Functions

context-apiproviderusereducer