Why do client routers model not-found as an entry in the route table rather than a render-time fallback?
answer
- a client cannot answer with a status
- every path must end in pixels
- matching failure is a routing outcome
- reachable on purpose from a parse step
- an empty list is not not-found
basics
~20 sFailing to match is a routing outcome, not a rendering detail. An entry is reached when matching fails or a matched entry rejects its value, before any route's work runs, and one place then owns the missing-page screen.
solid answer
~50 sA client router has no status code to return: whatever the URL says, something must appear on screen. Making not-found a real **entry** keeps that decision inside routing. It is reached in two ways: no pattern matched the path, or a matched entry's own parse or load step declared the thing missing and redirected the resolution there. Both land in one place with one design. The alternative — a matched screen that renders a not-found message under a condition — means the route *did* match, so its parse step, its data load and its side effects already ran for a URL that names nothing, and every screen grows its own version of the same empty state. As an entry, it also sits at a chosen depth in the table, so the surrounding chrome you want is kept and the one you do not is not.
go deeper
Know that an unmatched URL must still show something, and that the missing-page screen belongs to the route table rather than being written again inside each page.
Explain the two ways the entry is reached — nothing matched, or a matched entry declared the thing missing — and why a render-time check means the route's work already ran.
Show the operational side: separate not-found from errors, log the offending path, and use a spike on one URL shape to find a renamed pattern something still links to.
Own the policy: which failures may resolve to not-found at all, who writes the copy and the way out, and how the table stays total over paths as the app's URL surface changes.
## Two ways to say not found There are two shapes a client app can take for the missing-page case, and they are not equivalent. 1. **A table entry.** The route table contains an entry whose job is the not-found screen. It is selected when matching finds nothing else, and it can also be selected deliberately when a matched entry decides the thing the URL names does not exist. 2. **A render-time fallback.** Some matched screen renders a not-found message when a condition holds — no record came back, the identifier looked wrong. The second is not a routing decision at all. It happens after routing already concluded that this URL is served by that screen. ## Why the failure belongs in the table On the server, a request that matches nothing has an obvious answer: a response with a status and no body to design. A client router does not have that option. A navigation always ends with pixels, so the router must be able to name an outcome for every path, including the ones the app has never heard of. An entry is how the table expresses that: the table becomes **total** over paths, and the not-found case stops being an accident. Being an entry brings concrete properties: - **It is reachable on purpose.** A parse step that rejects an identifier, or a load that finds nothing, can resolve to the not-found entry instead of returning a value. One outcome, one screen, from many causes. - **Nothing else has run.** No data load for a nonexistent record, no analytics event claiming a page view of a page that does not exist, no side effect from a screen that should never have been chosen. - **It has a place in the tree.** Because it is an entry, you choose how deep it sits, and therefore how much of the surrounding structure stays around it. - **It is inspectable.** The table can be read back — printed, tested, walked by tooling — and the not-found case is part of that inventory rather than a condition buried in a component. - **One design, one owner.** Copy, illustration, and the link back out live once. ## Two different failures that share one screen | Cause | Where it is detected | What the router does | |---|---|---| | No pattern matches the path | matching | select the not-found entry | | Pattern matched, value is malformed | the entry's parse step | fail the parse, resolve to not-found | | Pattern matched, value is well formed, nothing exists | the entry's load step | resolve to not-found | | Matched and loaded, but the list is empty | the screen | an empty state, **not** not-found | The last row is the distinction people miss. An empty list is a successful answer about an existing thing; not-found means the URL names something that is not there. Showing the not-found screen for an empty result tells the user their link is broken when it is not. ## What the entry needs - **A clear way in from a matched route.** A parse or load step must be able to declare not-found rather than throwing something generic, otherwise the case gets mixed up with real errors. - **A distinction from the error screen.** Not-found is an expected, ordinary outcome; an unhandled failure is not. Same visual family, different message and different instrumentation. - **A way out.** A link to a stable place, and ideally a search or a suggestion, because the user usually arrived from an old link rather than by mistyping. - **Honest instrumentation.** Log the path that produced it. A spike of not-found on one shape of URL is how you discover a renamed pattern that something still links to. ## Traps - Rendering not-found **inside** a screen that already fetched, so the request happens for a URL that names nothing. - Treating any failed load as not-found, which hides real failures behind a reassuring screen. - Treating not-found as an error, so monitoring fills with noise and real errors are lost in it. - Leaving no entry at all, so an unmatched path renders a blank frame — the worst outcome, because it looks like a crash and gives the user nothing to do. - Forgetting that a shared or bookmarked URL is the usual source, and writing copy that blames the user for typing it wrong.
- How is not-found different from the error screen, and why keep them apart?Not-found is an expected outcome of an unmatched or stale URL; an error screen means something went wrong that the app did not plan for. Keeping them apart gives the user the right message and the right way out, and keeps monitoring honest — a steady trickle of not-found is normal, while the same volume of errors would be an incident.
- A screen loads a list and the list is empty. Should that render the not-found entry?No. The URL named something that exists and the answer was legitimately empty, so the screen owns an empty state with a way to add or adjust filters. Reserve not-found for a URL that names an identity that is not there; using it for an empty result tells the user their link is broken when it is fine.
saying these in an interview costs you the question
- Renders not-found inside a screen that already fetched its data
- Treats every failed request as not-found, hiding real failures
- Shows the not-found screen for an empty list
- Reports not-found as an application error and drowns monitoring in noise
- Leaves an unmatched path rendering nothing at all
- Assumes the user mistyped rather than followed an old link