The Problem: "Prop Drilling"
Imagine you have a deeply nested component tree. The top-level App component holds the current user's information. A UserProfile component, nested 5 levels deep, needs to display that user's name.
Without Context, you would have to pass the user prop through every single intermediate component, even the ones that don't use it. This is called prop drilling, and it can make your code verbose and hard to maintain.
JavaScript
// Prop Drilling Example
function App() {
  const user = { name: 'Alice' };
  return <Layout user={user} />;
}
function Layout({ user }) {
  return <MainContent user={user} />;
}
function MainContent({ user }) {
  return <UserProfile user={user} />;
}
function UserProfile({ user }) {
  return <h1>{user.name}</h1>;
}
The Layout and MainContent components don't care about the user object, but they are forced to act as intermediaries.
The Solution: The Context API
The Context API provides a way to pass data through the component tree without having to pass props down manually at every level.
It involves three main steps:
1. Create a Context: First, you create a context object using React.createContext(). You can provide a default value.
JavaScript
// src/contexts/ThemeContext.js
import { createContext } from 'react';
// The default value ('light') is only used if a component
// tries to consume the context without a Provider above it.
export const ThemeContext = createContext('light');
2. Provide the Context Value: Next, you wrap a part of your component tree with the context's Provider. The provider component accepts a value prop. Any component inside this provider can now access this value.
JavaScript
// src/App.js
import React, { useState } from 'react';
import { ThemeContext } from './contexts/ThemeContext';
import Toolbar from './Toolbar';
function App() {
  const [theme, setTheme] = useState('dark');
  return (
    // Any component inside ThemeContext.Provider can now get the 'theme' value.
    <ThemeContext.Provider value={theme}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}
3. Consume the Context Value: Finally, any child component (no matter how deeply nested) can read the value from the context using the useContext Hook.
JavaScript
// src/components/ThemedButton.js
import React, { useContext } from 'react';
import { ThemeContext } from '../contexts/ThemeContext';
function ThemedButton() {
  // The useContext Hook reads the context value from the nearest Provider.
  const theme = useContext(ThemeContext);
  const style = {
    background: theme === 'dark' ? '#333' : '#FFF',
    color: theme === 'dark' ? '#FFF' : '#333',
  };
  return <button style={style}>I am a themed button!</button>;
}
The ThemedButton component can now access the theme value directly without it being passed through Toolbar or any other intermediate components. Prop drilling is solved!
Context as a Dependency Injection Pattern
This pattern is a form of dependency injection. The ThemedButton component is not responsible for creating or fetching the theme; it simply declares that it depends on the ThemeContext. The App component is responsible for injecting that dependency (the theme value) into the tree via the Provider.
This decouples your components, making them more reusable and easier to test.