[{"data":1,"prerenderedAt":108},["ShallowReactive",2],{"qa-\u002Freact\u002Fstate-and-data-flow\u002Fprop-drilling-composition":3},{"page":4,"siblings":92,"blog":105},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":20,"order":21,"path":22,"questions":23,"questionsCount":82,"related":83,"seo":84,"seoDescription":85,"stem":86,"subtopic":87,"topic":88,"topicSlug":89,"updated":90,"__hash__":91},"qa\u002Freact\u002Fstate-and-data-flow\u002Fprop-drilling-composition.md","Prop Drilling Composition",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","React","react",{"subtopicSlug":19},"prop-drilling-composition",true,4,"\u002Freact\u002Fstate-and-data-flow\u002Fprop-drilling-composition",[24,29,33,37,41,45,49,53,57,61,65,70,74,78],{"id":25,"difficulty":26,"q":27,"a":28},"prop-drilling-what","easy","What is prop drilling in React?","**Prop drilling** is the practice of passing data through multiple\nintermediate components that **don't need it themselves**, just so it\ncan reach a deeply nested consumer.\n\n```jsx\n\u002F\u002F data flows through App → Page → Section → Widget → Button\n\u002F\u002F Page, Section, and Widget never use `user`, they just pass it on\nfunction App()     { return \u003CPage user={user} \u002F> }\nfunction Page({user})    { return \u003CSection user={user} \u002F> }\nfunction Section({user}) { return \u003CWidget user={user} \u002F> }\nfunction Widget({user})  { return \u003CButton user={user} \u002F> }\nfunction Button({user})  { return \u003Cspan>{user.name}\u003C\u002Fspan> }\n```\n\nThe intermediate components become tightly coupled to data they don't\nown, making refactoring painful.\n\n**Rule of thumb:** If a prop passes through two or more components that\ndon't use it, you have a prop drilling problem worth addressing.\n",{"id":30,"difficulty":26,"q":31,"a":32},"prop-drilling-problems","What are the main problems caused by prop drilling?","1. **Coupling** — intermediate components must accept and forward props\n   they don't care about, creating unnecessary dependencies.\n2. **Refactor friction** — adding, removing, or renaming a prop means\n   updating every intermediate layer.\n3. **Readability** — component signatures grow bloated with pass-through\n   props, obscuring the component's real interface.\n4. **Testing burden** — intermediate components must be tested with\n   props they don't use, just to satisfy type signatures.\n\n```jsx\n\u002F\u002F Every time the shape of `user` changes, all three layers must be\n\u002F\u002F updated — even Page and Section which never read user directly\nfunction Page({ user, theme, locale, featureFlags }) { ... }\n```\n\n**Rule of thumb:** The deeper the tree and the more layers a prop\ncrosses, the more painful prop drilling becomes.\n",{"id":34,"difficulty":14,"q":35,"a":36},"prop-drilling-composition-fix","How can component composition solve prop drilling without using Context?","Pass **pre-rendered children** (JSX elements) down instead of raw data.\nThe top-level component builds the element with full access to its own\nscope; intermediate components just place it.\n\n```jsx\n\u002F\u002F Instead of threading `user` through Page → Section → Widget:\n\nfunction App() {\n  return (\n    \u003CPage>\n      \u003CSection>\n        \u003CWidget>\n          \u003CButton user={user} \u002F>   {\u002F* App has direct access to user *\u002F}\n        \u003C\u002FWidget>\n      \u003C\u002FSection>\n    \u003C\u002FPage>\n  )\n}\n\n\u002F\u002F Intermediates accept children and don't know about user\nfunction Page({ children })    { return \u003Cmain>{children}\u003C\u002Fmain> }\nfunction Section({ children }) { return \u003Csection>{children}\u003C\u002Fsection> }\nfunction Widget({ children })  { return \u003Cdiv>{children}\u003C\u002Fdiv> }\n```\n\nReact calls this **inversion of control** — the parent controls what\nthe children render.\n\n**Rule of thumb:** Try the `children` pattern before reaching for\nContext — it often eliminates drilling with zero new abstractions.\n",{"id":38,"difficulty":26,"q":39,"a":40},"prop-drilling-children-prop","What is the `children` prop and how does it enable composition?","`children` is the implicit prop React passes for any JSX content placed\nbetween a component's opening and closing tags. Components that render\n`{children}` act as **layout containers** — they don't need to know\nwhat's inside.\n\n```jsx\nfunction Card({ title, children }) {\n  return (\n    \u003Cdiv className=\"card\">\n      \u003Ch2>{title}\u003C\u002Fh2>\n      \u003Cdiv className=\"card-body\">{children}\u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F Usage\n\u003CCard title=\"Profile\">\n  \u003CAvatar user={user} \u002F>\n  \u003CBio text={user.bio} \u002F>\n\u003C\u002FCard>\n```\n\n`Card` renders the user-specific components without knowing anything\nabout `user` itself.\n\n**Rule of thumb:** If a component is a structural wrapper (card, modal,\nlayout), always accept `children` rather than a specific content prop.\n",{"id":42,"difficulty":14,"q":43,"a":44},"prop-drilling-slots","What is the \"slot\" pattern in React and how does it differ from just using children?","The slot pattern uses **named props** (each holding a JSX element) to\nplace content in specific regions — like a header slot, footer slot, and\nbody slot — without the component needing to know about the data inside.\n\n```jsx\nfunction Layout({ header, sidebar, children }) {\n  return (\n    \u003Cdiv className=\"layout\">\n      \u003Cheader>{header}\u003C\u002Fheader>\n      \u003Caside>{sidebar}\u003C\u002Faside>\n      \u003Cmain>{children}\u003C\u002Fmain>\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F Caller builds each slot with its own data\n\u003CLayout\n  header={\u003CNavBar user={user} \u002F>}\n  sidebar={\u003CUserMenu user={user} \u002F>}\n>\n  \u003CFeed posts={posts} \u002F>\n\u003C\u002FLayout>\n```\n\n`Layout` remains generic; the caller composes what goes into each slot.\n\n**Rule of thumb:** Use named slots when a layout component has multiple\ndistinct areas that need different content.\n",{"id":46,"difficulty":14,"q":47,"a":48},"prop-drilling-context-solution","When does composition not solve prop drilling and Context becomes necessary?","Composition fails (or becomes awkward) when:\n- The consuming component is **many levels deep** and you would need to\n  thread JSX through too many layers to compose it at the top.\n- The consuming component is in a **third-party library** or a **route**\n  you don't control.\n- The data is needed by **many unrelated subtrees** (e.g. current user,\n  theme, locale).\n\n```jsx\n\u002F\u002F If Button is inside a deeply nested third-party table cell,\n\u002F\u002F you can't easily pass it as a child from the top.\n\u002F\u002F Context is the right tool here.\nconst UserContext = createContext(null)\n\nfunction App() {\n  return (\n    \u003CUserContext.Provider value={user}>\n      \u003CThirdPartyTable columns={columns} \u002F>\n    \u003C\u002FUserContext.Provider>\n  )\n}\n\nfunction CustomCell() {\n  const user = useContext(UserContext)\n  return \u003CButton>{user.name}\u003C\u002FButton>\n}\n```\n\n**Rule of thumb:** Try composition first. Reach for Context when the\nconsumer and the data owner are architecturally disconnected.\n",{"id":50,"difficulty":14,"q":51,"a":52},"prop-drilling-render-props","What are render props and how do they help with composition?","A **render prop** is a prop whose value is a function that returns JSX.\nThe component calls the function at render time, passing internal state\nor logic to the caller.\n\n```jsx\nfunction MouseTracker({ render }) {\n  const [pos, setPos] = useState({ x: 0, y: 0 })\n\n  return (\n    \u003Cdiv onMouseMove={e => setPos({ x: e.clientX, y: e.clientY })}>\n      {render(pos)}    {\u002F* caller decides what to show *\u002F}\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F Usage — no prop drilling; the caller gets the data it needs\n\u003CMouseTracker render={({ x, y }) => \u003CCursor x={x} y={y} \u002F>} \u002F>\n```\n\nRender props share logic without a component hierarchy, but they can\nlead to deeply nested JSX (\"callback hell\"). Custom hooks have largely\nreplaced them for logic sharing.\n\n**Rule of thumb:** Prefer custom hooks for sharing stateful logic; use\nrender props only when you need to inject JSX into a component that\ncontrols lifecycle.\n",{"id":54,"difficulty":14,"q":55,"a":56},"prop-drilling-hoc","How do Higher-Order Components (HOCs) relate to prop drilling?","An **HOC** is a function that takes a component and returns a new\ncomponent with extra props injected. They were the pre-hooks solution\nfor sharing logic without drilling, but they add indirection and can\nconflict with prop names.\n\n```jsx\nfunction withUser(Component) {\n  return function WrappedComponent(props) {\n    const user = useContext(UserContext)\n    return \u003CComponent {...props} user={user} \u002F>\n  }\n}\n\nconst ProfileWithUser = withUser(Profile)\n\u002F\u002F Profile receives user without the caller drilling it\n```\n\nHOCs are still used in third-party libraries (React-Redux's `connect`)\nbut custom hooks are preferred for new code.\n\n**Rule of thumb:** Write a custom hook first. Only reach for an HOC\nwhen the consuming component is a class component or you need to inject\ninto a component you don't own.\n",{"id":58,"difficulty":14,"q":59,"a":60},"prop-drilling-avoid-global","Is it always wrong to pass a prop through an intermediate component?","No. Passing props **one level down** is perfectly fine and is often the\nclearest approach. Prop drilling is only a problem when it spans many\nlayers **and** the intermediate components have no other reason to know\nabout the prop.\n\n```jsx\n\u002F\u002F Fine — one level, clear intent\nfunction ProductPage({ product }) {\n  return \u003CProductCard product={product} \u002F>\n}\n\n\u002F\u002F Problem — three layers, none of them use `user`\nfunction ProductPage({ product, user }) {\n  return \u003CProductCard product={product} user={user} \u002F>\n}\nfunction ProductCard({ product, user }) {\n  return \u003CSaveButton user={user} \u002F>\n}\nfunction SaveButton({ user }) {\n  return \u003Cbutton disabled={!user.isPremium}>Save\u003C\u002Fbutton>\n}\n```\n\n**Rule of thumb:** One level is fine. Two levels — consider composition.\nThree or more — seriously evaluate Context or composition.\n",{"id":62,"difficulty":14,"q":63,"a":64},"prop-drilling-colocate","How can co-locating state close to where it's used reduce prop drilling?","If state is lifted higher than it needs to be, every component between\nthe owner and the consumer has to forward it. Moving state **down** to\nthe component that actually needs it eliminates the intermediate passes.\n\n```jsx\n\u002F\u002F ❌ modal state lives in App, drills through Header → Nav → MenuButton\nfunction App() {\n  const [isMenuOpen, setIsMenuOpen] = useState(false)\n  return \u003CHeader isMenuOpen={isMenuOpen} onToggle={setIsMenuOpen} \u002F>\n}\n\n\u002F\u002F ✅ menu state lives where it belongs — no drilling\nfunction Nav() {\n  const [isMenuOpen, setIsMenuOpen] = useState(false)\n  return \u003CMenuButton open={isMenuOpen} onToggle={setIsMenuOpen} \u002F>\n}\n```\n\nState co-location is the inverse of \"lift state up\" — lift only when\ntwo siblings need the same state; keep it local otherwise.\n\n**Rule of thumb:** Before reaching for Context, ask \"Can I just move\nthis state down to where it's used?\"\n",{"id":66,"difficulty":67,"q":68,"a":69},"prop-drilling-zustand","hard","How does a library like Zustand solve prop drilling compared to Context?","Zustand creates a **store outside React's component tree**. Any\ncomponent can subscribe directly with a selector and only re-renders\nwhen its selected slice changes — no Provider needed, no intermediate\nforwarding.\n\n```jsx\nimport { create } from 'zustand'\n\nconst useUserStore = create(set => ({\n  user: null,\n  setUser: user => set({ user }),\n}))\n\n\u002F\u002F Any component anywhere in the tree — no Provider, no drilling\nfunction Button() {\n  const user = useUserStore(state => state.user)\n  return \u003Cspan>{user?.name}\u003C\u002Fspan>\n}\n```\n\nUnlike Context, Zustand subscriptions are selector-scoped — a component\nonly re-renders when its selected slice actually changes.\n\n**Rule of thumb:** Add Zustand (or Redux Toolkit) when Context re-render\nperformance becomes a real problem or when you need devtools \u002F\nmiddleware.\n",{"id":71,"difficulty":26,"q":72,"a":73},"prop-drilling-summary","What are the four main solutions to prop drilling, ordered by complexity?","1. **Co-location** — move state down so it lives closer to the consumer.\n2. **Composition \u002F children** — pass pre-rendered JSX or named slots so\n   intermediate components don't need to know about the data.\n3. **Context API** — share data across a subtree without prop passing,\n   best for infrequently changing global values.\n4. **External store** (Zustand, Redux) — selector-scoped subscriptions\n   for frequently changing data with many consumers.\n\n```\nCo-location   → simplest, no overhead\nComposition   → good DX, no new abstractions\nContext       → built-in, okay for infrequent updates\nStore library → scalable, selector performance, devtools\n```\n\n**Rule of thumb:** Solve at the lowest complexity level that works.\nDon't jump to Redux because you read about prop drilling; try\ncomposition first.\n",{"id":75,"difficulty":26,"q":76,"a":77},"prop-drilling-types","What TypeScript pattern helps make prop-forwarding components less painful?","Use **rest props spread** (`...props`) combined with a specific type\nfor your own props, so the component forwards everything else without\nlisting every forwarded prop explicitly.\n\n```tsx\nimport { ButtonHTMLAttributes } from 'react'\n\ninterface IconButtonProps extends ButtonHTMLAttributes\u003CHTMLButtonElement> {\n  icon: string   \u002F\u002F own prop\n}\n\nfunction IconButton({ icon, ...rest }: IconButtonProps) {\n  return (\n    \u003Cbutton {...rest}>\n      \u003Ci className={`icon-${icon}`} \u002F>\n      {rest.children}\n    \u003C\u002Fbutton>\n  )\n}\n\n\u002F\u002F Caller can pass onClick, disabled, aria-label without drilling each one\n\u003CIconButton icon=\"save\" onClick={save} disabled={!dirty} \u002F>\n```\n\nThis pattern is especially useful for wrapper components around native\nelements.\n\n**Rule of thumb:** Extend `HTMLAttributes` and spread `...rest` for any\ncomponent that wraps a native element.\n",{"id":79,"difficulty":67,"q":80,"a":81},"prop-drilling-compound","How does the compound component pattern reduce prop drilling inside a component family?","Compound components use **implicit Context** to share state between a\nparent and its child sub-components — the caller doesn't need to wire\nup any props between them.\n\n```jsx\nconst AccordionCtx = createContext(null)\n\nfunction Accordion({ children }) {\n  const [openId, setOpenId] = useState(null)\n  return (\n    \u003CAccordionCtx.Provider value={{ openId, setOpenId }}>\n      \u003Cdiv>{children}\u003C\u002Fdiv>\n    \u003C\u002FAccordionCtx.Provider>\n  )\n}\n\nAccordion.Item = function Item({ id, title, children }) {\n  const { openId, setOpenId } = useContext(AccordionCtx)\n  const isOpen = openId === id\n  return (\n    \u003Cdiv>\n      \u003Cbutton onClick={() => setOpenId(isOpen ? null : id)}>{title}\u003C\u002Fbutton>\n      {isOpen && \u003Cdiv>{children}\u003C\u002Fdiv>}\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F Usage — no prop drilling between Accordion and Item\n\u003CAccordion>\n  \u003CAccordion.Item id=\"a\" title=\"Section A\">Content A\u003C\u002FAccordion.Item>\n  \u003CAccordion.Item id=\"b\" title=\"Section B\">Content B\u003C\u002FAccordion.Item>\n\u003C\u002FAccordion>\n```\n\n**Rule of thumb:** Use the compound component pattern for component\nfamilies (tabs, accordions, dropdowns) that need shared state but want\na clean JSX API.\n",14,null,{"description":11},"React prop drilling interview questions — what it is, why it hurts, and how to fix it with composition, Context, children props, and slot patterns.","react\u002Fstate-and-data-flow\u002Fprop-drilling-composition","Prop Drilling and Composition","State and Data Flow","state-and-data-flow","2026-06-24","lSE3CKtjSCZ0yfQ1sTV2AfbxI21sv2LXPhVBiKlk7jg",[93,97,100,104],{"subtopic":94,"path":95,"order":96},"Lifting State Up","\u002Freact\u002Fstate-and-data-flow\u002Flifting-state",1,{"subtopic":98,"path":99,"order":12},"Context API","\u002Freact\u002Fstate-and-data-flow\u002Fcontext-api",{"subtopic":101,"path":102,"order":103},"Controlled vs Uncontrolled Components","\u002Freact\u002Fstate-and-data-flow\u002Fcontrolled-vs-uncontrolled",3,{"subtopic":87,"path":22,"order":21},{"path":106,"title":107},"\u002Fblog\u002Freact-prop-drilling-composition-guide","Prop Drilling and Composition in React — A Complete Guide",1782244100924]