Menu

Prop Drilling in React: What It Is and How to Avoid It

Prop drilling is passing a prop through components that do not use it, only so a component further down can. See it in a runnable example, why it hurts as an app grows, and the fixes in the order to try them: composition, context, then a state library.

This page includes runnable editors - edit, run, and see output instantly.

Prop drilling is passing a prop through components that do not use it, only so a component further down the tree can. Here user travels from App through Page and Sidebar to reach Avatar, the one component that reads it.

Four levels, and only the last one cares about user. The Console shows Page and Sidebar running for a value they never display. Click the button and they log again.

Why it hurts

Two or three levels is fine. Prop drilling becomes a problem as the tree grows:

  • The middle components carry props they do not use. Page and Sidebar now have a user prop in their signature, so you cannot read them without wondering what it is for.
  • Every change touches every layer. Add user.avatarUrl or a second value like onLogout, and you edit each component on the path, not just the two ends.
  • Moving a component breaks the chain. Move Avatar into a different branch and you must thread user through a new path.
  • It is easy to drop a prop. Forget to forward it in one layer and the deep component gets undefined, with no error at the layer that lost it.

Try it: add an onLogout function in App that Avatar should call. You will edit all four components.

Fix 1: composition with children

This fix is the one people skip. Instead of passing data down so a deep component can render with it, let the component that owns the data render the deep component itself, and pass the finished element down as children (or any other prop). The middle layers render a slot and never see user.

Page and Sidebar no longer know a user exists. App creates Avatar where user lives and hands it down as a ready element. Layout components like these become reusable, and adding onLogout is now a change in App and Avatar only. Both still log on every click, because App passes them a new element each time it renders: composition simplifies the code, not the number of renders. The children page covers more slot patterns.

Composition works when the component that owns the data can also decide what the deep part looks like. It does not help when the deep component sits inside a library or a structure the owner does not render.

Fix 2: context

When many components at different depths need the same value (the theme, the signed-in user, the current language), put it in context. Any component under the provider reads it with useContext, and nothing in between changes.

Avatar and Greeting read the user from two different depths, and Page and Sidebar take no props. Add a third reader anywhere under Page and it works without touching the rest. The cost: the data flow is no longer visible in the props, and every reader renders when the value changes. The useContext page covers providers, defaults and those re-renders.

Fix 3: a state library

For large app state that many components read and that changes often (a cart, a document editor, live data), a state library such as Redux Toolkit, Zustand or Jotai lets each component subscribe to just the slice it uses. That avoids both drilling and the "every consumer renders" cost of one big context. It is also another dependency and another set of concepts, so reach for it when the first two fixes stop being enough, not to fix a prop that travels three levels.

// Zustand, for comparison (not available in the editor here)
const useCart = create((set) => ({
    items: [],
    add: (item) => set((s) => ({ items: [...s.items, item] })),
}));

function CartCount() {
    const count = useCart((s) => s.items.length); // renders only when the count changes
    return <span>{count}</span>;
}

When prop drilling is fine

Passing props is how React data flows, and it is the easiest pattern to read: you can follow a value from the top to the bottom by looking at the JSX. A prop that goes through one or two components that use part of it, or through a short chain of closely related components, is not a problem to solve. Lift state to the closest common parent, pass it down, and only change approach when the middle layers start carrying props they have no use for.

Frequently Asked Questions

What is prop drilling in React?

Passing a prop down through several layers of components that do not use it, only so a component deep in the tree can read it. Each middle component accepts the prop and forwards it.

Is prop drilling bad?

Not by itself. Passing a value two or three levels down is explicit and easy to follow. It becomes a problem when many layers carry props they never use, so every rename or new field touches files that have nothing to do with it.

How do I avoid prop drilling?

Try composition first: let the parent build the deep component and pass it down as children or another prop, so the middle layers never see the data. If many components at different depths need the value, use context. For large state that changes often, consider a state library.

Is context the only fix for prop drilling?

No, and it is often not the best one. Composition with children removes the drilling without adding a context, and it keeps the data flow visible in the code.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED