React useContext for Theming — Dark Mode and Theme Switching

Implement theme switching with React useContext. Complete guide to creating a ThemeProvider, consuming context, and avoiding unnecessary re-renders in themed applications.

Context Hooks

Detailed Explanation

Building a Theme System with useContext

useContext reads a value from a React context, enabling components to share data without prop drilling. Theming is one of its most iconic use cases.

Creating the Theme Context

import { createContext, useContext, useState } from "react";

type Theme = "light" | "dark";

interface ThemeContextValue {
  theme: Theme;
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextValue | null>(null);

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setTheme] = useState<Theme>("dark");

  const toggleTheme = () => {
    setTheme(prev => prev === "dark" ? "light" : "dark");
  };

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

Creating a Custom Hook

Wrapping useContext in a custom hook improves DX and provides runtime validation:

export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error("useTheme must be used within a ThemeProvider");
  }
  return context;
}

Consuming the Theme

function Header() {
  const { theme, toggleTheme } = useTheme();

  return (
    <header className={theme === "dark" ? "bg-black" : "bg-white"}>
      <button onClick={toggleTheme}>
        Switch to {theme === "dark" ? "light" : "dark"}
      </button>
    </header>
  );
}

Performance Optimization

Every consumer re-renders when the context value changes. To optimize:

  1. Memoize the value object with useMemo to prevent new references on every render
  2. Split contexts by update frequency (e.g., separate ThemeContext from UserContext)
  3. Use React.memo on child components that do not consume context directly

Use Case

Use useContext for theming, authentication, locale, feature flags, and any data that many components at different nesting levels need to access without explicit prop passing.

Try It — React Hooks Reference

Open full tool