The first client pass over server-rendered markup finds nodes that do not match its description. Which code causes that, and how do you fix it?
answer
- the first pass must line up exactly
- environment, clock, locale, corrected markup
- structure breaks lockstep, values can be patched
- render late, mark unmatched, or fix the structure
basics
~20 sFour classes cause it: a value only one side can read, a clock or random source, locale- or timezone-dependent formatting, and parser-corrected markup. Fix it by rendering the volatile part after attach, marking the subtree unmatched, or repairing the nesting.
solid answer
~50 sClaiming existing nodes only works if the client's **first** described output matches the markup node for node, because the runtime walks the two in lockstep and assumes the markup is right. Four families of code break that: a value read from the host environment that the producer did not have; a clock or a random number, so timestamps, relative times and generated identifiers differ; locale, timezone or currency formatting resolved differently on each side; and a description whose nesting is invalid, so the parser moved or closed nodes and the document never contained the described shape. The runtime then patches the node or discards that subtree and rebuilds it. The remedies: render the volatile part only after the attach pass, mark the subtree as deliberately unmatched so the client value is accepted quietly, or fix the structure — the last one is a bug, not something to silence.
go deeper
Know the rule: markup produced elsewhere is only adopted if the browser's first output describes the same tree. Values from the clock, the environment or locale formatting are the usual offenders.
Name the four classes and say what the runtime does with each — patch a differing value, discard and rebuild a differing structure — and why the second is the expensive one.
Work backwards from the reported node to the input that differs, and pick the fix on purpose: shared input, post-attach render, or a narrow deliberate mark. Treat corrected markup as a bug to fix.
Decide policy: mismatch reports should fail a build or an end-to-end check rather than being silenced per component, because the cost is paid as discarded upstream work and post-paint layout shift on real users.
## Why the first pass has to match When a renderer adopts nodes it did not create, it walks the description and the existing nodes together, position by position, assuming each node it finds is the one the description intends. It has no way to *search*: the whole saving of the mode comes from not re-writing what is already there. So the invariant is strict — **the first described output in the browser must be the same tree the upstream render produced**, node for node. After that first pass the runtime owns the tree again and ordinary update rules apply, which is why the constraint is so often misremembered as a general rule about output. ## The four classes that break it 1. **A value only one side can read.** Viewport size, a stored preference, a device or capability check, a value that exists in the browser but not where the markup was produced. The producer renders one branch, the browser computes another. 2. **A clock or a random source.** A timestamp, a relative time string, a duration, a shuffled order, a generated identifier. The two renders happen at different moments and, for random values, from different draws. 3. **Locale-, timezone- or currency-dependent formatting.** The markup is produced in the producer's locale and timezone; the browser formats in the user's. A date, a number separator, a currency symbol or a sort order differs even though the input data is identical. 4. **Markup the parser corrected.** The description nests elements in a way the host's parser does not allow, so it closes, moves or drops nodes while building the tree. The document then legitimately does not contain the described structure, and the walk cannot line up. This one is the most confusing to debug, because nothing in the code looks volatile. A fifth cause deserves a mention: **different data**. If the producer and the browser resolve the same query against different state — a cache, a mutation in between, a per-request value not passed along — the output differs for reasons that have nothing to do with rendering. ## What the runtime does about it Two responses, chosen by case: - **Patch in place.** Text or an attribute differs but the structure lines up: keep the node, write the client's value, carry on, and usually report the difference. - **Discard and rebuild.** The structure differs, so lockstep is impossible: drop that subtree and re-run it in create mode. The second is what makes mismatches expensive. The upstream work for that region is thrown away *after* it was painted, so the user sees content replaced, often with a layout shift, and the closer the mismatch is to the root the larger the rebuilt region. A mismatch is therefore both a correctness signal and a performance defect. ## The remedies | Fix | What it does | What it costs | Use it for | |---|---|---|---| | Render the volatile part after attach | both sides output the same neutral version; the real value appears in a post-attach callback | one extra frame, and layout shift if the two sizes differ | environment reads, clocks, locale formatting where a neutral placeholder is acceptable | | Mark the subtree as deliberately unmatched | the runtime accepts a difference there, keeps the client value, and does not report it | the region is not really shared work any more; too broad a mark hides real defects | a small, genuinely client-specific value such as a rendered local time | | Pass the producer's value through | the browser's first pass uses the same input the producer used, then updates later if needed | the value has to be serialised with the page | data differences, and identifiers that must be stable | | Fix the structure | the description becomes valid, so the parser stops correcting it | none — it was a bug | corrected markup, always | The second row is the one most often abused. Marking a subtree as unmatched is a narrow tool: it makes a *value* difference acceptable, and it does not rescue a structural difference — the walk still fails. Reaching for it whenever a warning appears converts a loud, cheap-to-fix problem into a silent one. ## How to work one out in practice 1. Read the reported node and ask which of the four classes could make *that* node differ. 2. Check the description for invalid nesting before suspecting anything dynamic; a structural report with no dynamic values is almost always the parser. 3. For a value difference, find the input: is it read from the environment, from a clock, from a formatter with an implicit locale or zone, or from data that the two sides resolved separately? 4. Choose the fix by whether the value is *meant* to be shared. If it is, make both sides compute it from the same input. If it genuinely cannot be, move it after the attach pass or mark it. ## Where frameworks differ Runtimes vary in how loudly they report, how much they patch before giving up, and whether a failure is scoped to a subtree boundary or escalates toward the root. Some emit position markers so the walk can resynchronise. What none of them can do is guess which of the two trees was right — that is why the first pass is the client's contract to keep.
- Does the matching requirement apply to every later update too?No — only to the first pass over markup the runtime did not create. Once that pass completes, the runtime holds a description for every position and owns the tree, so subsequent updates compare against its own bookkeeping. That is why a component may render whatever it likes from a clock or the environment after the attach pass, and only the first output is constrained.
- Why is rendering the volatile part after attach not free?The first frame shows a neutral placeholder and the real value appears one frame later, so that content is visibly late. If the placeholder and the value have different sizes, the correction shifts the layout around it. It also means the region is effectively browser-only work: the upstream render produced markup that was always going to be replaced.
- How would you keep mismatches from reaching users at all?Make them fail something automated. Run the attach pass in an end-to-end check on the main routes with reports treated as errors, and keep per-component suppression rare enough to review. The failure mode otherwise is silent: a region is discarded and rebuilt after paint on real devices, and nobody sees a warning because nobody is looking at a console.
saying these in an interview costs you the question
- Thinks every mismatch discards the whole page and rebuilds it
- Marks subtrees as unmatched as the routine fix for any warning
- Believes marking a subtree rescues a differing tree structure
- Blames a random or timestamp value when the report is structural
- Assumes the constraint applies to every later update, not just the first pass