Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Context

Data usually flows from parent to child through props. But if a value is needed five levels down, it has to be passed through every component in between — this is called prop drilling. Context solves it differently: the value becomes available to any component inside the wrapper, at any depth.

Creating a context

Let us add a light and dark theme switch to the blog. A context is created once in its own file — src/ThemeContext.js:

// src/ThemeContext.js
import { createContext } from 'react';
 
export const ThemeContext = createContext('light');

The argument of createContext is the default value. A component with no wrapper above it gets that value.

Providing a value

The context itself serves as the wrapper component. The value is set with the value prop, usually in src/App.jsx:

// src/App.jsx
import { ThemeContext } from './ThemeContext.js';
import PostList from './components/PostList.jsx';
 
export default function App() {
  return (
    <ThemeContext value="dark">
      <PostList />
    </ThemeContext>
  );
}

In React 19 the context is written as a tag directly. In older versions the wrapper was ThemeContext.Provider — such code is still around and still works.

Reading the value

Any component inside reads the value with the useContext hook. Create src/components/ThemedBox.jsx:

// src/components/ThemedBox.jsx
import { useContext } from 'react';
import { ThemeContext } from '../ThemeContext.js';
 
export default function ThemedBox({ children }) {
  const theme = useContext(ThemeContext);
 
  return <div className={`box box-${theme}`}>{children}</div>;
}

If a post card somewhere inside PostList is wrapped in ThemedBox, it gets the class box-dark. The components in between do not need to know about the theme.

Context with state

To make the theme switchable, put state into the context together with its setter:

// src/App.jsx
import { useState } from 'react';
 
export default function App() {
  const [theme, setTheme] = useState('light');
 
  return (
    <ThemeContext value={{ theme, setTheme }}>
      <ThemeSwitch />
      <PostList />
    </ThemeContext>
  );
}
// src/components/ThemeSwitch.jsx
import { useContext } from 'react';
import { ThemeContext } from '../ThemeContext.js';
 
export default function ThemeSwitch() {
  const { theme, setTheme } = useContext(ThemeContext);
 
  return (
    <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      Theme: {theme}
    </button>
  );
}

The button switches the theme, and every component reading the context re-renders with the new value. useContext now returns an object, so in ThemedBox write const { theme } = useContext(ThemeContext).

When to use it

Context suits data needed in many places: the theme, the interface language, the current user. For data of one or two components, props are enough — with them it is easier to see where a value came from.

Common mistake

A component that reads a context outside its wrapper silently gets the default value:

export default function App() {
  return (
    <>
      <ThemeContext value="dark">
        <PostList />
      </ThemeContext>
      <Footer /> {/* the theme here is 'light' */}
    </>
  );
}

Make sure the wrapper covers every component that needs the value. It is often placed at the very top — in src/main.jsx or at the root of App.

Thanks for reading!

Donate to the site — we work hard for you.