React Context: When to Use It

Context is powerful but often misused. Here's when it makes sense and when it doesn't.

What Context Solves

Prop drilling. Passing data through many component levels:

// Without context - prop drilling
<App>
  <Layout user={user}>
    <Header user={user}>
      <UserMenu user={user} />
    </Header>
  </Layout>
</App>

Every component in the chain needs to pass `user` even if they don't use it.

Context Solution

const UserContext = createContext(null);

function App() {
  const [user, setUser] = useState(null);
  
  return (
    <UserContext.Provider value={{ user, setUser }}>
      <Layout>
        <Header />
      </Layout>
    </UserContext.Provider>
  );
}

function UserMenu() {
  const { user } = useContext(UserContext);
  return <span>{user?.name}</span>;
}

Now `UserMenu` accesses user directly. No drilling.

Good Use Cases

Theme

const ThemeContext = createContext('light');

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

Current User

function useCurrentUser() {
  return useContext(UserContext);
}

Locale/i18n

const { locale, t } = useContext(LocaleContext);

Feature Flags

const flags = useContext(FeatureFlagContext);

Bad Use Cases

Frequently Changing Data

Context re-renders all consumers when value changes.

// ❌ Bad - updates every second, all consumers re-render
<Context.Provider value={{ time: Date.now() }}>

Component-Local State

If state only matters to one component tree, just use local state.

// ❌ Overkill
<ModalOpenContext.Provider value={isOpen}>

// ✅ Just lift state to common parent
function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <Button onClick={() => setIsOpen(true)} />
      {isOpen && <Modal onClose={() => setIsOpen(false)} />}
    </>
  );
}

Complex State Logic

For complex state with many actions, consider useReducer or a state library.

Performance Tip

Split contexts to avoid unnecessary re-renders:

// ❌ One big context
const AppContext = createContext({ user, theme, locale, notifications });

// ✅ Separate contexts
const UserContext = createContext(null);
const ThemeContext = createContext('light');
const LocaleContext = createContext('en');

Now changing theme doesn't re-render components that only use user.

The Rule

Use context for:

  • Data that many components need
  • Data that rarely changes
  • "Global" concerns (theme, user, locale)

Don't use context for:

  • Frequently updating data
  • Data used by only a few nearby components
  • Complex state management

Back to Journal