skip to content

In a nested layout chain, how do you decide what belongs on a shared level rather than repeated on each route beneath?

level: principalimportance: should knowfreq 44%

answer

  1. leverage in both directions
  2. the subtree pays, not the route
  3. child parameters cannot be read from above
  4. split the level before blocking it
  5. shared level equals shared failure domain

basics

~20 s

Price it against the whole subtree. A shared level's data step, bundle, blocking time and failure surface are paid by every route beneath it, so put work there only when every route genuinely needs it and the cost is bounded.

solid answer

~50 s

A shared level is leverage in both directions: declared once, but **paid for by every route beneath it**. So the test is not 'is this reused twice'. I ask whether every route in the subtree needs it, whether it depends on parameters owned lower down - in which case it cannot live above, since the parent is reused and will not re-run when those change - whether it blocks or can be deferred, and what it does to the failure and rendering characteristics of the whole subtree. If something is shared but expensive I would rather split the level: cheap, stable chrome above, the volatile or slow part in its own level lower down. And I would treat shared levels as a platform surface with a named owner, a size and latency budget, and review by the teams downstream of them.

go deeper

for a junior

The takeaway is that a shared level is not free: whatever you put there is loaded and run for every route below it, so it should be something all of those routes really need.

for a middle

Be able to name the concrete costs - data step per entry, code in every route's payload, latency in front of the subtree - and the correctness limit that data varying per child cannot live above the child.

for a senior

Show the levers rather than a verdict: split the level, defer behind a boundary, cache with a stated freshness policy, or restructure so a group of routes does not inherit the cost at all.

for a principal

Own the chain as a platform surface. Assign each shared level an owner and a budget, make opting out a routing decision rather than a flag, and name accretion on the outermost level as the failure mode you are guarding against.

## The asymmetry that drives every decision Putting something on a shared level buys consistency and a single place to change it. What it costs is multiplied by the size of the subtree: the level's data step runs on every entry into that subtree, its code ships with every route beneath it, its latency sits in front of every one of those routes, and its failure is their failure. A level three segments deep with two children is a cheap bet. The outermost level, sitting in front of the entire application, is the most expensive real estate in the app. The decision is therefore economic, not aesthetic. 'It appears on more than one page' is not sufficient justification. ## Four questions before anything moves up 1. **Does every route beneath actually need it?** If two routes out of nine use a value, seven are paying for it. Duplication in two places is often cheaper than a tax on nine. 2. **Does it depend on something owned lower down?** A parent is reused while its children change, so it does not re-run when a child's parameters change. Data that varies per child cannot live above the child and stay correct - this is a correctness constraint, not a preference. 3. **Does it block, and can it be deferred?** Work high in the chain sits in front of everything below. If it must be shared but is slow, it can often be moved behind its own pending boundary so the rest of the chain is not held up, or cached so the cost is paid rarely. 4. **What does it do to the subtree's characteristics?** A shared level that must be computed per request tends to drag the routes below it into the same mode; a shared level with no boundary of its own turns its failures into subtree-wide failures. ## A decision table | Signal | Put it on a shared level | Keep it per route | |---|---|---| | Needed by every route beneath | Yes | - | | Varies with a child's parameters | No - it will not re-run | Yes | | Cheap, cacheable, stable | Yes | - | | Slow and on the critical path | Only behind its own boundary | Often yes | | Needed by two routes out of many | - | Yes, duplicate it | | Must be consistent across the area | Yes | - | ## Levers when something must be shared but is expensive - **Split the level.** Keep the chrome that every route needs above, and push the expensive or volatile part into a level closer to the routes that need it. - **Defer rather than block.** Let the shared level render its shell and resolve the slow part behind its own pending state, so the subtree is not waiting on it. - **Cache the shared read**, with an explicit freshness policy, so the cost is amortised over the subtree instead of repeated per entry. - **Make it client-owned.** A value that must be live is often better subscribed or polled than resolved by a level that the router reuses and therefore does not refresh. - **Restructure the tree.** If a group of routes must not pay for a level, the honest fix is usually to give them a chain that does not include it, rather than adding conditionals inside a shared level. ## The organisational half At scale the chain is also an ownership diagram. A shared level is a **shared dependency and a shared failure domain**: one team's change to it lands in every team's routes beneath. Things worth settling explicitly: - **Who owns each shared level**, and who must review changes to it. - **A budget** for the levels near the top - bytes added to every route, milliseconds added in front of every route - checked in the same place other budgets are. - **Escape hatches** for teams whose routes genuinely should not pay, which means a route structure that lets them opt out rather than a flag inside the shared level. - **A convention about conditionals.** A shared level full of 'if this route, then' branches is a signal that the level is in the wrong place; the chain, not an if-statement, is the mechanism for scoping. ## The failure mode to name in an interview The app that accretes everything onto its outermost level - every provider, every global fetch, every analytics client - because each addition looked free in isolation. Nothing in the chain resists it, no single change is unreasonable, and the bill arrives as a slow first render on every route and a failure surface no one owns. Preventing that is exactly the judgment the question is asking about.

  • Why can a value that depends on a child's parameters not live on the level above?
    Because a parent is reused while its children change. It matched identically, so it is not re-rendered and its data step is not run again - the value it resolved for the previous child stays on screen. The level that owns a value has to be a level the relevant navigations actually invalidate.
  • A shared level is slow but genuinely needed everywhere. What are the options?
    Cache it with an explicit freshness policy so the cost is rare; move the slow part behind its own pending boundary so it stops blocking the subtree; or split the level, keeping the cheap chrome shared and pushing the expensive part down to the routes that actually depend on it. Removing it is only an option if 'needed everywhere' survives scrutiny.
  • How would you keep the outermost level from accreting work?
    Give it a named owner and an explicit budget - bytes and milliseconds added to every route - and require that additions there be reviewed by the teams downstream. Anything that only some routes need belongs on a level those routes share, not at the top, and a growing list of route conditionals in that level is the signal that the rule has been ignored.

saying these in an interview costs you the question

  • Moves anything used twice up a level without pricing the routes beneath
  • Thinks a shared level's data step runs once per session rather than per entry
  • Ignores that a shared level is one latency and failure path for the whole subtree
  • Hoists data so children can share it, then wonders why it never refreshes
  • Treats the outermost level as the natural home for anything global
  • Scopes a shared level with route conditionals instead of restructuring the chain