Vivian Voss

Hydration

react javascript performance architecture

The Copy Cat ■ Episode 7

In 1995, PHP rendered HTML on the server and sent it to the browser. The browser displayed it. That was the entire architecture. Terribly straightforward, one must admit.

In 2026, the industry renders HTML on the server, sends it to the browser, then ships 558 KB of JavaScript to rebuild the very same DOM. They call this hydration. One rather wishes they were joking.

The Original

Server-Side Rendering. CGI, PHP, JSP, ASP. The server produced HTML. The browser consumed it. Forms, links, progressive enhancement. No second pass required. Quite dull, really. It simply worked.

The model was straightforward: a request arrived, the server assembled HTML from templates and data, and the browser rendered it. Interactivity came from forms and links. Where JavaScript was needed, it enhanced what was already there. The page was usable before a single script loaded. This was not a design philosophy. It was the only option. And it happened to be correct.

1995: Server-Side Rendering Server renders HTML HTML Browser displays it Done. One request. One response. One render. Interactive on arrival. The page worked before JavaScript loaded. Quite the concept.

The Detour

Then Single-Page Applications moved rendering to the client. The server sent an empty <div id="root"> and a JavaScript bundle. The browser downloaded the bundle, parsed it, executed it, fetched data via API calls, and finally rendered the page. Performance collapsed. SEO collapsed. First Contentful Paint times tripled. But the developer experience was, one is assured, quite pleasant.

The industry noticed SPAs were slow. The fix: render on the server again. But they refused to let go of the framework. So now the server renders HTML, the browser displays it, then the framework downloads, parses the DOM a second time, rebuilds the component tree in memory, and attaches event listeners to every interactive element.

The same page. Built twice. Shipped thrice (HTML, JavaScript bundle, serialised state). The architectural equivalent of building a house, handing over the keys, then demolishing it and rebuilding it while the owner watches.

React v16 formalised the ceremony in 2017 with ReactDOM.hydrate().

2026: Server-Side Rendering with Hydration Server renders HTML HTML JS bundle Browser displays HTML Hydration 1. Download 558 KB JavaScript 2. Rebuild component tree in memory 3. Reconcile with existing DOM 4. Attach event listeners Visually complete on arrival. Interactive after hydration. Same page, built twice.

The Triple Data Problem

Hydration does not merely rebuild the DOM. It ships the same information three times:

  1. The rendered HTML (the page the user sees)
  2. The JavaScript templates (the component code that knows how to render it)
  3. The serialised state (JSON data embedded in the page so the framework can reconcile)

Three representations of the same content, downloaded over the same connection, processed by the same browser. Instagram ships 5 MB of JavaScript to display photographs. One suspects the photographs themselves are lighter.

The Cascade of Patches

Each attempt to fix hydration created the next problem. The history reads like a support ticket that keeps being escalated:

The cascade of patches SPAs move rendering to the client the decision Performance and SEO collapse the consequence Re-invent SSR inside the framework the patch Hydration blocks interactivity the cost Invent partial hydration the patch for the patch Requires top-down loading order the limitation Invent islands architecture the next patch Islands introduce coordination cost the next cost Each layer solves the problem created by the previous one. The original was solved in 1995.

Next.js re-invented SSR for React in 2016. Partial hydration followed. Astro introduced islands architecture in 2020, shipping JavaScript only for interactive components. Each iteration was cleverer than the last. None questioned whether the framework should be there in the first place.

The Architect's Confession

Misko Hevery, creator of Angular, built Qwik because hydration is, in his words, "pure overhead." His framework replaces hydration with resumability: the server serialises the application state, and the client resumes execution without replaying it. No re-render. No second pass.

Even the architects who popularised client-side rendering now build tools to escape its consequences. Rather telling, that.

The Modern Answer

Rust. Go. Any language that renders HTML on the server without the ceremonial re-enactment.

Askama for Rust: compile-time Jinja-like templates, type-checked at build, zero JavaScript. No runtime template engine. No virtual DOM. The compiler catches your typos before the server starts.

templ for Go: compiled HTML components with type safety and IDE support. No runtime overhead. No JavaScript required. The template is a Go function. The output is HTML. That is the entire abstraction.

Need device-specific layouts? In my Rust backends (for example, this very site), the standard approach: one detection request sets a cookie. The server knows the client. Every response after that is byte-optimised for the device. No framework. No second pass. No hydration.

Need interactivity? A <script> tag. A fetch call. An event listener. Not a framework that rebuilds your page to make a button clickable.

Median JavaScript per page (HTTP Archive) 600 KB 450 KB 300 KB 150 KB 359 KB 558 KB 412 KB 613 KB 2019 2024 Mobile Desktop

The Irony

The server rendered your page. The browser displayed it. Then JavaScript arrived and insisted on doing it all again. Twenty years of engineering to return to where PHP started. The median JavaScript payload grew from 359 KB in 2019 to 558 KB in 2024. A 55% increase in five years, solving a problem that did not exist until client-side rendering created it.

PHP, for all its critics, never asked the browser to rebuild anything. CGI never demanded a second pass. The original server-side model rendered once and was done. The copy renders twice and calls it progress.

Quite the journey.