Verified against Claude Code · 2026-07-22
Fix prop drilling without reaching for Redux as the first move
Fixes a prop-drilling chain with composition or a narrowly-scoped context first, and only escalates to an external store if the data is needed outside the subtree or updates too frequently for context.
The prompt
Ready to copy — highlighted parts are example details you can swap.
You are fixing a prop-drilling problem in a React component tree. You try composition first, then a narrowly-scoped context, and only recommend an external store if neither one actually solves the real problem in front of you. COMPONENT TREE Layout receives user, theme, and onUpdateUser as props and passes all three to Sidebar, which passes all three to Panel, which passes all three to Widget, the only component that actually reads onUpdateUser and theme. DRILLED PROPS user, theme, onUpdateUser — passed through Layout, Sidebar, Panel, down to Widget. NEEDED OUTSIDE THIS SUBTREE No — user and theme are read elsewhere in the app, but through a separate top-level AuthContext already; this drilling is purely internal to the Layout subtree. UPDATE FREQUENCY theme changes a handful of times per session at most; user rarely changes at all during a session. FIX ORDER Check first whether the intermediate components that just forward the props even need to know about them at all — if a middle component only passes the drilled props through without reading any of them, consider passing the already-composed JSX down instead, as children or a render prop, so the data goes directly from the top to where it is actually used and the middle component's own prop list shrinks to nearly nothing. If composition does not fit because the intermediate components genuinely branch on the data — real conditional rendering based on it, not just passing it along untouched — create a context scoped to just this subtree, not a new top-level app-wide context, and provide it at the lowest common ancestor that actually has the data. Only recommend an external store if the same data is also needed outside this subtree per the cross-subtree requirement above, or if the update frequency makes context genuinely problematic because every consumer of a context re-renders on every value change regardless of which part of that value it reads. Do not default to "just use Redux" or "just use Zustand" as a first response — that is a deliberate escalation with a real cost in added dependency and indirection, applied only once the first two options have been checked and ruled out for a stated reason. When a scoped context ends up being the right fit, wrap its useContext call in a small custom hook that throws a clear error if used outside the provider, rather than having every consuming component call the raw useContext(SomeContext) directly and silently receive undefined if the provider is missing somewhere up the tree. Do not fix prop drilling by bundling several unrelated props into one options or config object passed down instead — that changes the drilling's shape without reducing it, and it actively hides which individual pieces of data each intermediate component or the final consumer actually needs. OUTPUT FORMAT 1. Which fix you applied and, explicitly, why each earlier option in the fix order did not fit — do not skip straight to the answer without showing the elimination. 2. The updated component tree with the fix applied, as real code. 3. The props list for each affected component, shown before and after — the actual point of the exercise is a visibly shorter list, and if it isn't visibly shorter, say why.
Customize
Optional — swap in your own details for the highlighted parts above.
Why this works
Passing already-composed children through the tree is React's own documented alternative to Context for the specific case where intermediate components don't need to read the drilled data, only pass it along — a component that receives finished JSX as a prop doesn't need to know the shape of user or theme at all, which shrinks the middle components' prop lists to essentially nothing rather than just relocating the same prop names into a useContext call one layer down. Scoping the fallback context to the exact subtree that needs it, instead of hoisting it into a new top-level provider, keeps the fix proportional to the actual problem and avoids widening the re-render blast radius to parts of the app that never had this issue in the first place. Making "try composition, then scoped context, then a store" an explicit sequence with a stated rule against defaulting to a store directly targets the most common overreaction in exactly this prompt category: reaching for the heaviest available tool as the first answer to a problem composition alone would have fixed for free, which is how small apps accumulate a state-management dependency and a mental-model tax that a two-line children prop would have avoided entirely. Wrapping the scoped context's useContext call in a dedicated hook that throws on a missing provider is the same defensive pattern that makes any context genuinely safe to consume: without it, a component that calls useContext outside its provider gets undefined silently, and the resulting bug — a crash on the first property access, or a mysteriously blank render — surfaces far from the actual mistake, which was forgetting to wrap a route or a test in the provider, not anything in the component that crashed. Flagging the bundle-everything-into-one-object move as a non-fix matters because it is the single most common way prop drilling gets "solved" without actually being solved: the prop count at each layer drops to one, which looks like success in a shallow before-and-after comparison, but every intermediate component still has to know the bundled object's shape to pass it through, and the actual coupling the fix order exists to reduce — components depending on data they don't use — is completely unchanged underneath the cosmetic prop-count improvement.
What you get back
Fix applied: composition. Sidebar and Panel never read user, theme, or onUpdateUser — they only forwarded them — so Layout now renders Widget directly, passed as pre-composed children through Sidebar and Panel: <Sidebar><Panel><Widget user={user} theme={theme} onUpdateUser={onUpdateUser} /></Panel></Sidebar>. Composition fit because neither intermediate component branches on the drilled values; context wasn't needed at all, and a store was never on the table since the data isn't needed outside this subtree and barely changes. Props before: Sidebar(user, theme, onUpdateUser, children?), Panel(user, theme, onUpdateUser, children?). Props after: Sidebar(children), Panel(children) — both down to a single prop.
Verified against
Claude Code Sonnet 4.6 · 2026-07-22
ChatGPT GPT-5.1 · 2026-07-28
Changelog
- 2026-07-22 — Initial publish, verified against Claude Code (Sonnet 4.6) and ChatGPT (GPT-5.1).
Need this built into your business?
If a prompt isn't enough — custom software, built and maintained for you — that's Scult's day job.
EXPLORE CUSTOM SOFTWARE
