Suspected from the code while planning Batch 17, then confirmed in Chromium:
every page logged React error #418, a hydration mismatch. React answers a
mismatch by discarding the server DOM and re-rendering the page on the client.
So the prerender ran, crawlers received it, and every visitor's browser threw it
away and did the work again.
Two causes, both ours:
1. prerender hoisted the JSON-LD scripts out of the body into <head>. React
hoists only async scripts with a src, so on the client that script stays
where its component renders it. The DOM and the client's first render
therefore disagreed on every page that emits structured data. JSON-LD is
valid anywhere in the document, so it now stays where React puts it. Title,
meta and link tags are still hoisted, because React hoists those itself.
2. main.jsx rendered sonner's <Toaster> in the first client pass, and the server
entry never rendered one, so the client expected a <section> the prerendered
HTML did not have. It mounts after hydration instead, which costs nothing: a
toast can only follow an interaction.
Measured in a real browser, all 18 sitemap pages, before and after: hydration
errors 18 to 0, other console and page errors 0. Each page keeps its
server-rendered DOM (an h1 stamped before hydration survives), and still has
exactly one head title and one canonical. Structured data is unchanged in
substance: 27 JSON-LD blocks across 19 pages, and OAI-SearchBot still receives
Service, BreadcrumbList and Organization on the contact-center page.
Closes#226.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>