skip to content

In a nested route table, how does a parent entry's pattern constrain which child entries can match a URL?

level: middleimportance: should knowfreq 45%

answer

  1. a chain, not a single winner
  2. the parent consumes a prefix
  3. children see only the remainder
  4. a failing parent skips its whole branch
  5. captured values accumulate downward

basics

~20 s

The parent consumes a path prefix, so its children are matched only against the remainder, and only if the parent matched at all. A failing parent skips its whole branch, and its captured parameters stay visible below.

solid answer

~50 s

Matching in a nested table walks the path left to right and produces a **chain** of entries rather than one. The parent's pattern claims a prefix and captures its parameters; each child's pattern is written relative to that prefix and is tested against the **remainder**. Two consequences matter in practice. If the parent does not match, none of its children are tried — the whole subtree is skipped in one step, which is what makes a large table cheap to match and a misplaced branch invisible. And a child pattern that repeats the parent's prefix produces an effective path with the prefix twice, so the URL the author meant can never be reached. An entry for the empty remainder handles the parent's own path. Because parameters accumulate down the chain, a parent's value is best converted once for the whole subtree.

go deeper

for a junior

Remember that a nested child's pattern is only the piece of path it adds, and that it is matched against what the parent left over.

for a middle

Explain matching as consuming the path to build a chain: a failing parent eliminates its whole branch, the empty remainder needs its own entry, and captures accumulate downward.

for a senior

Diagnose from the mechanism: an unreachable pattern usually means a repeated prefix or a branch nested under the wrong parent, and a parent's value should be converted once for the subtree.

for a principal

Decide the shape of the tree deliberately — which prefixes exist, who owns each level's parameters and validation — because the nesting is the app's URL structure, not an implementation detail.

## Matching a tree, not a list A flat route table is a list of complete patterns. A nested table is a tree: an entry may own children whose patterns are **relative** to it. Matching then does not pick one winner; it walks down the tree consuming the path and produces an ordered **chain** — parent, child, grandchild — together with the parameters each level captured. That chain is what the rest of the app consumes, and understanding how it is built explains most nested-routing surprises. ## What the parent consumes 1. The matcher tests the parent's pattern against the **start** of the path. 2. If it does not match, the parent and **everything beneath it** are eliminated together. No child is examined at all. 3. If it matches, the matcher records the parameters the parent captured and hands the **remainder** of the path down to the children. 4. Each child repeats the process against that remainder, and so on until the path is fully consumed. So a parent is a filter as well as a screen. It is why a table with hundreds of entries does not cost hundreds of comparisons, and it is why a branch nested in the wrong place looks dead: its patterns are perfectly correct, but the prefix above them never matches the URLs it was written for. ## The empty remainder When the parent's pattern consumes the whole path, the remainder is empty. That case needs an entry of its own — the child that matches nothing left over — otherwise the parent's own path resolves to a parent with no child underneath it and the screen has a hole in the middle. This is the mechanism behind the familiar bug where a section's landing path renders chrome and nothing else. ## Parameters accumulate | Level | Pattern (relative) | Consumes | Parameters visible there | |---|---|---|---| | parent | `/teams/:teamId` | `/teams/42` | teamId | | child | `members` | `members` | teamId | | grandchild | `:memberId` | `7` | teamId, memberId | Every level sees its own captures and those of its ancestors, which is what lets a deep screen build a request from several identifiers without threading them through props. It also means the conversion of a parent's captured value into typed data belongs **on the parent entry**: done once, it is correct for the whole subtree, and a malformed value can be rejected before any child's work begins. Duplicating that conversion in each child is how the same identifier ends up validated three different ways. ## Where nesting goes wrong - **Repeating the prefix in a child.** A child written as the full path under a parent that already claims that prefix yields an effective path containing the prefix twice. It is a common cause of a pattern that exists, reads correctly and cannot be reached. - **Nesting for looks rather than for the URL.** Nesting is a statement about paths first. Grouping unrelated URLs under a parent whose prefix they do not share makes them unmatchable. - **No entry for the empty remainder.** The parent's own path renders an incomplete screen. - **Assuming a child sees only its own parameter.** It sees the ancestors' too, and relying on a name that a parent also captures invites a collision. - **Two levels capturing the same parameter name.** The inner one usually wins and the outer value silently disappears from view. - **Validating a parent's value in every child.** Rules drift, and the malformed case is handled differently depending on which child was opened. ## What to take away The parent's pattern is a **gate**, not decoration: it decides both whether the branch is considered and what its children are matched against. Write child patterns as the piece of path they add, convert each captured value once at the level that captured it, and give every parent an entry for the empty remainder. How a router chooses between several candidate patterns that all match — specificity against declaration order — is a separate subject; the point here is that nesting decides which candidates exist at all.

  • Why is a parent's pattern the right place to convert its captured value into typed data?
    Because everything beneath it depends on that value. Converting once at the level that captured it means every child receives the typed value, a malformed one is rejected before any child's work starts, and there is a single failure branch. Converting in each child produces divergent rules and a different outcome depending on which child was opened.
  • What goes wrong when two levels of a chain capture the same parameter name?
    The inner capture generally shadows the outer one, so code that reads the name gets the nearer value and the ancestor's value quietly disappears. Give the levels distinct names that say what they identify, so a deep screen can read both and a reader can tell which level owns which value.

It is like sorting post: the building number gets you into one block and the floor is only read after that, so a wrong block means no flat inside it is ever checked.

saying these in an interview costs you the question

  • Writes a child pattern as the full path under a parent that already claims the prefix
  • Expects children to be tried even when the parent's pattern did not match
  • Nests entries for visual grouping rather than for shared path prefixes
  • Forgets an entry for the empty remainder, so the parent's own path is incomplete
  • Re-converts the parent's captured value inside every child
  • Reuses one parameter name at two levels of the same chain