What is "prop drilling"?
Definition
Prop drilling is the process where data (props) is passed down the component tree through several intermediate levels that don't use this data themselves, but simply "pass it further".
A simple example
Suppose you have the structure:
<App>
└── <Parent>
└── <Child>
└── <Grandchild>You want to pass data from App -> to Grandchild.
Without context (through props):
function Grandchild({ user }) {
return <p>Hello, {user.name}!</p>;
}
function Child({ user }) {
return <Grandchild user={user} />;
}
function Parent({ user }) {
return <Child user={user} />;
}
export default function App() {
const user = { name: "Tim" };
return <Parent user={user} />;
}Here Parent and Child don't use user at all,
they simply pass it further.
This is prop drilling.
Why this is bad
| Problem | What happens |
|---|---|
| Code noise | Every intermediate component is forced to have extra props |
| Hard to maintain | Changing the structure requires changing every level |
| Isolation decreases | Components become dependent on data they don't use |
| Reusability drops | "Dumb" components are forced to know about unnecessary props |
| Hard to read | The "chain" of props makes the app's logic harder to understand |
Illustration
App
├── user (stored here)
↓
Parent
↓
Child
↓
Grandchild (uses it)The whole tree is "drilled" with props from top to bottom - hence the term "drilling".
A more realistic example
<App>
<Navbar />
<Sidebar />
<Main>
<PostList>
<PostCard user={user} />
</PostList>
</Main>
</App>If user is only needed in PostCard, but has to be passed through Main and PostList, that's already prop drilling hell.
How to solve the prop drilling problem
1. React Context API
The most standard way to get rid of drilling is to create a "global data source".
import { createContext, useContext } from "react";
const UserContext = createContext(null);
function Grandchild() {
const user = useContext(UserContext);
return <p>Hello, {user.name}!</p>;
}
function App() {
const user = { name: "Tim" };
return (
<UserContext.Provider value={user}>
<Grandchild />
</UserContext.Provider>
);
}Now Grandchild gets user directly from context,
not through a chain of props.
2. State management (Redux, Zustand, Jotai, Recoil)
If there's a lot of data and it's needed in different parts of the app, it's better to move it into a global store:
// store.js (example with Zustand)
import { create } from "zustand";
export const useUserStore = create(set => ({
user: { name: "Tim" },
setUser: (user) => set({ user }),
}));
// Grandchild.jsx
import { useUserStore } from "./store";
function Grandchild() {
const user = useUserStore(state => state.user);
return <p>Hello, {user.name}!</p>;
}Now there's no need to "drill" props - data is accessible from any component.
3. Custom hooks
If context or Zustand feel like "heavy artillery", you can simply make a custom hook to hide the passing:
function useUser() {
const [user, setUser] = useState({ name: "Tim" });
return { user, setUser };
}
function App() {
const userStore = useUser();
return <Parent userStore={userStore} />;
}
function Grandchild({ userStore }) {
const { user } = userStore;
return <p>{user.name}</p>;
}Cleaner than passing every individual prop, but prop drilling remains - just simplified.
4. Composition instead of props
Sometimes you can pass components directly through children:
function Layout({ header, footer }) {
return (
<div>
<header>{header}</header>
<main>Content</main>
<footer>{footer}</footer>
</div>
);
}
function App() {
return (
<Layout
header={<Header user={{ name: "Tim" }} />}
footer={<Footer />}
/>
);
}This doesn't remove prop drilling globally, but it makes data passing local and more manageable.
When prop drilling is acceptable
You don't always need to avoid it at any cost.
If the component tree is shallow (1-2 levels). If the prop is only needed a bit lower. If there isn't much data and it rarely changes.
Context and a store are needed when the problem is systemic, not occasional.
Summary
| Criterion | Prop drilling |
|---|---|
| What it is | Passing props through a chain of intermediate components |
| Why it's bad | Pollutes the code, reduces isolation and reusability |
| When it's acceptable | For shallow and simple cases |
| How to solve it | Context API, Zustand, Redux, custom hooks |
| Main rule | "If a prop passes through more than 2 levels, think about context" |
Main idea:
Prop drilling is when data is drilled through unnecessary components. The solution is to move the data source higher up and give direct access to it.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.