advanced 3 min answer

A server-rendered page uses islands: static HTML with independently hydrated interactive components. In production, one island's hydration throws because a third-party script mutated the DOM before React attached. What does the user see, and what stops it?

hydrationislandserror-boundariesthird-partysilent-failure
Show the full answer Hide the answer

Second by second, what happens

The HTML arrives and renders. The page looks completely correct, because the server produced the right markup and the browser painted it. Then the island's hydration runs, encounters a DOM that does not match what it expected, and throws.

What the user gets depends on the framework and the failure: a component that never becomes interactive, a component that discards the server HTML and re-renders from scratch with a visible flash, or in the worst case an exception that aborts hydration for the rest of the tree. In every version, the page looks finished and does nothing when clicked. A button that is present and inert is worse than a missing button, because the user keeps pressing it.

Where it amplifies

  • Nothing errors in a way you see. The server responded 200, no request failed, and the loading metrics are excellent — the page painted fast, because painting is all that happened.
  • The failure is population-specific. It occurs only for users who got the third-party script variant that mutates the DOM, so it can be 4% of sessions and invisible in aggregates.
  • Retrying does not help. The same script runs again on reload, so a user who refreshes gets the same dead page and concludes the product is broken.
  • Conversion damage exceeds the apparent scope. If the affected island is "add to basket", the page is decorative.

What stops it

  1. An error boundary per island, and a recovery action inside it. The boundary's job is not to log; it is to leave the user something that works — a plain link to a server-rendered page that performs the same action without JavaScript. The pattern that survives is that the critical action has a non-hydrated fallback path.
  2. Client-side telemetry for hydration outcomes. Emit a beacon when an island hydrates successfully, and count failures against attempts per island. This is the metric almost nobody has, and it is the only thing that makes the failure visible: a hydration success rate below 99.5% for an island is an incident.
  3. Isolate third-party scripts from the DOM you own. Do not let a tag manager inject into containers your framework controls; give third parties their own containers, load them after hydration, and reject unlisted origins with a content security policy.
  4. Keep the island small enough that its failure is survivable, which is the actual architectural argument for islands over whole-page hydration: the blast radius of a hydration failure is the island.
  5. A synthetic check that asserts interactivity, not appearance. A test that loads the real page with the real tags and clicks the primary action catches this class; a screenshot comparison never will.

What would have to be true for it to self-heal

Almost nothing self-heals here, because the server is behaving correctly and the client has no mechanism for noticing that it is inert. The closest thing is a watchdog: if the primary action's island has not reported successful hydration within a few seconds, replace it with the fallback link. That is a deliberate design, not an emergent property.

When this is the wrong thing to defend against

If the page has no critical action — a marketing page, a blog post, documentation — an island that fails to hydrate costs a nicety and the cheapest response is to log it and move on. The defences above are for the paths that carry revenue or that a user cannot complete another way. And if the application is a client-rendered tool behind a login where nothing works without JavaScript, fallback paths are a fiction: there the right investment is an error boundary with an honest message and a reload, because a plausible-looking dead page is the failure you are trying to avoid.