Day one of a new product. You add a “Save” button on the settings page; success flashes a toast. Three lines of useState in the same component. Ship it.

Week two. The PM asks: “When the webhook retries succeed, show a toast.” The retry code runs inside useWebhookQueue, six components deep, in a totally different subtree. It has no way to reach setToastMessage — the state lives at the settings page, not at the hook's ancestor. Three instincts kick in; each one fails a different invariant.

Below is the live prop-drill tower. App at the top; SaveButton at the bottom. Six seats in between that know nothing about toasts. Tap the button. Watch nothing happen. Then flip “drill the prop” and watch the red snake thread showToast through every intermediate. Add the sibling source and watch the tax compound.

The render treedrill: off
<App />root — owns state once lifted
<Layout />page chrome wrapper
<SettingsPage />route-level container
<SettingsSidebar />nav split
<AccountPanel />form section
<AccountForm />form element
<ActionRow />button container
<SaveButton />the actual toaster

Tax: 0 — no drill, no tax

Flip “drill the prop” to thread showToast through every intermediate. Watch the count rise.

The only fix a single \`useState\` allows

Flip to thread showToast through every intermediate seat. Red tags show the cost. Untoggle and the button's taps go silent again.

A second source

Add useWebhookQueue in the sibling Dashboard subtree. State has to live at App (shared ancestor); second chain threads through its own 6 intermediates. Tax compounds.

Zero tax, zero toast. useState is local — no wire reaches the button.

Undrilled, the button tap goes silent — the six intermediate seats have no prop carrying setToastMessage, and the button has no way to read state from a component six levels above it. React's data flow is unidirectional via props. Without a wire, the call is impossible.

Drilled, the toast works — but look at what paid for it. Every intermediate seat now forwards a showToast prop it doesn't care about. SettingsSidebar isn't a form, Layout isn't a route — neither of them should know the notification api exists. Refactor AccountForm next week and the toast six levels down breaks. Add the sibling source and the tax doubles — ActivityFeed, JobQueue, RetryWatcher all start forwarding a prop they don't want, for a hook that was the whole point of adding the toast in the first place. One toaster is fine. Two is annoying. Three is malpractice.

Tap the button first (feel “nothing happens”). Flip “drill the prop” (watch the snake). Add the sibling source (feel the tax compound). Then ask the gate question.