skip to content

In a nested route chain, if one level fails or is still loading, how much of the rendered page does its state replace?

level: middleimportance: should knowfreq 58%

answer

  1. the state is scoped, not global
  2. the nearest boundary wins
  3. levels above keep rendering around it
  4. no boundary means it bubbles upward
  5. the outermost level has no ancestor

basics

~20 s

Only the slot of the nearest enclosing boundary. The levels above it keep rendering, so shared chrome stays on screen, and if no level near the problem declares a boundary the state bubbles upward and replaces more of the page.

solid answer

~40 s

Pending and failure states are **scoped by position in the chain**. A level that declares its own pending state shows it in its own slot while its work is unfinished; a level that declares its own failure state renders it in that slot when its work throws, and the levels above carry on rendering around it. If the failing level declares nothing, the state is handled by the **nearest ancestor that does**, and that ancestor's whole subtree is replaced instead - which is why an app with a single boundary at the very top blanks entirely on any failure. The outermost level is the exception in the other direction: nothing sits above it, so a failure it raises has no ancestor to contain it.

go deeper

for a junior

Know that a loading or error state belongs to a level and fills that level's own region, so the rest of the page can stay on screen while one part is unfinished or broken.

for a middle

Explain the upward walk: the nearest boundary at or above the failure renders, everything outside it keeps its markup, and a level's own fallback covers its children rather than itself.

for a senior

Argue about placement from real incidents: which failures should cost a region and which a page, why one boundary at the top is an outage multiplier, and what a contained fallback must tell the user.

for a principal

Treat boundary placement as the failure-domain map of the app. Decide which parts of the chain are allowed to take the page down, and hold the outermost level to the strictest standard because nothing contains it.

## Scoping is positional Every level in a route chain can declare two extra states beside its normal output: what to show while its own work is unfinished, and what to show when that work fails. Both are **scoped to where the declaring level sits**. Whatever markup the levels above it produced is already on screen and stays there; only the slot belonging to the declaring level is filled with the pending or failure output. The rule that decides how much of the page changes is therefore not 'how bad was the failure' but 'where is the nearest boundary'. ## Walking the chain upward 1. A level's work fails - its data step rejects, or its rendering throws. 2. The framework looks for the **nearest boundary at or above that level**. 3. That boundary's slot is replaced with its failure output. 4. Everything **outside** that slot - the levels above it - keeps its rendered markup and, in most frameworks, keeps working: navigation controls in a shared header still respond, because those components were never torn down. The same walk describes pending states: a level with its own pending state shows it inside its slot while the rest of the page is already visible; a level without one is covered by an ancestor's, which means a larger region of the page is occupied by a placeholder. | Where the boundary sits | What the user sees when a deep level fails | |---|---| | On the failing level itself | Full chrome, with one region replaced | | On a section level above it | That whole section replaced, app chrome intact | | Only at the outermost level | Effectively the whole page replaced by one fallback | | Nowhere | The framework's own last-resort handling takes the page | ## Consequences worth designing for - **Granularity is a choice you make by file placement.** Adding a boundary near a fragile leaf is usually a one-file change that converts a whole-page failure into a contained one. - **One boundary at the top is the common anti-pattern.** It satisfies 'we handle errors' while guaranteeing that any failure anywhere costs the user the entire screen, including navigation away from the problem. - **A boundary cannot catch its own level's failure.** If a level's own data step throws, the state that renders is its ancestor's - because the failing level is the thing being replaced. This is the detail that surprises people: the fallback they wrote on that level covers its children, not itself. - **The outermost level has nothing above it.** A failure it raises cannot be contained by the chain, so whatever the framework does as a last resort is what the user gets. Keep that level's work minimal and defensive for exactly this reason. - **A contained failure leaves a partial page.** The rest of the chain rendered successfully, so the user sees a working app with one broken region. That is usually the goal, but the fallback then has to say enough for the user to know what is missing and what to do. - **Pending states have a symmetrical cost.** A placeholder high in the chain hides a large region of otherwise-ready content; a placeholder per level keeps the page usable but can make it flicker in pieces. ## Choosing where the boundaries go A workable default is one boundary per region a user could still use if the rest broke, plus one on anything known to be flaky: - Give a level its own pending state when its work is slow enough that the rest of the page is worth showing first. - Give the level **above** a fragile data step a failure state, so that step can fail without taking its whole section with it. - Resist wrapping every single level; a placeholder per level turns one loading page into a page that flickers into existence in six pieces. - Keep the outermost level thin, because both its waiting time and its failures are uncontainable by anything else in the chain. Both extremes are real failure modes. One boundary at the top makes every fault total; a boundary on every level makes the user watch the page assemble itself and never see a stable frame. The middle - boundaries on the seams a user would recognise as separate parts of the screen - is what most teams converge on. ## Where the framework-specific details live Meta-frameworks differ in the file or declaration that marks a level as a boundary, in whether a boundary must run on the client, and in what status the response carries when the failure happens during a server render. The positional rule - **nearest boundary at or above the failure wins, everything outside it keeps rendering** - is the part that transfers between them, and it is the part an interviewer is checking you understand.

  • Which state renders when a level's own data step throws?
    Its ancestor's, not its own. That level is the thing being replaced, so the boundary that contains the failure has to sit above it. A fallback declared on a level covers what renders inside it; giving a fragile level its own containment means putting a boundary on the level above, or splitting the work down into a child that can fail on its own.
  • What is wrong with declaring a single failure state at the top of the chain?
    It turns every failure, however local, into a whole-page failure: the shared chrome inside that boundary goes with it, so the user cannot even navigate away from the broken region. Boundaries are cheap and positional - putting them near the parts that can realistically fail is what converts an outage into a degraded corner of the page.

Boundaries are the fuse box of the chain: a fault trips the nearest fuse on that circuit and only the sockets behind it go dark. Fit one fuse at the mains and every small fault blacks out the whole house.

saying these in an interview costs you the question

  • Thinks any failure in the chain blanks the whole page regardless of boundaries
  • Believes a level's own fallback catches that level's own data-step failure
  • Assumes every level has an implicit fallback, so placement does not matter
  • Says a pending state always covers the whole page while any level is loading
  • Treats one top-level boundary as sufficient error handling for the app