Lesson 09 named the hydration tax. This is the menu of cures — the spectrum from hydrate everything, eagerly to don't hydrate at all: selective & progressive hydration, islands, partial hydration via RSC, and resumability (Qwik). The Lead skill is knowing which lever each pulls.
Server rendering gives you fast first paint and SEO (Lesson 09). But the HTML it sends is dead — no event handlers, no state. Hydration is the process of making it alive on the client. The whole field below is a single arms race: make that process cheaper. The strategies are just answers to “how much do we hydrate, and when?” [web.dev]
| Lever | Idea | Strategy |
|---|---|---|
| Hydrate all, now | the baseline — re-run the whole tree eagerly on load | classic SSR rehydration |
| Hydrate later | defer hydration until idle / interaction / visible | lazy / selective / progressive |
| Hydrate less | only the interactive bits; leave static HTML alone | islands · partial hydration (RSC) |
| Hydrate never | serialize state+listeners into HTML, resume on demand | resumability (Qwik) |
“Hydration is the bill SSR hands you on the client. Every strategy here is one move: hydrate later, hydrate less, or don't hydrate at all.”
On load, the framework walks the server HTML and “adopts” it. Mechanically: [web.dev]
Three things make it costly, and they compound: it's eager (happens on load whether or not you ever interact), all-at-once (one long task, not interruptible by default), and it runs on the main thread of a possibly-weak device. It also re-does work the server already did — and ships the data twice (once as rendered HTML, once as serialized state for the JS to re-read). That long task is a leading cause of poor INP and the “uncanny valley”: the page looks ready but taps do nothing until hydration finishes. [patterns.dev]
“Hydration re-runs the whole app on the client just to attach handlers — eager, all-at-once, on the main thread. Fast paint, but the slow phone pays twice. That's the bill we're cutting.”
Don't hydrate a component until it's needed — when it scrolls into view, when the browser is idle, or on first interaction. React's streaming does this selectively per <Suspense> boundary and prioritises whatever the user clicks first (Lesson 09 §3).
Hydrate the tree in prioritised pieces over time instead of one blocking pass — critical/above-the-fold first, the rest as the main thread frees up. Breaks the single long task into many small ones, helping INP.
Ship the page as mostly static HTML with isolated interactive “islands” (a search box, a carousel) that hydrate independently. Astro/Marko. Most of the page ships zero JS. §3.1.
React Server Components render server-only and ship 0 JS; only "use client" subtrees hydrate (Lesson 09 §4). The React-ecosystem flavour of “hydrate less.”
Coined by Jason Miller: think of the page as a sea of static, server-rendered HTML with small islands of interactivity embedded in it. Each island is its own tiny app that hydrates on its own schedule; the static sea never hydrates at all. The framework gives you per-island directives for when: [Jason Miller] [Astro]
// Astro — the directive picks the hydration trigger per island
<Header /> // static — 0 JS, never hydrates
<SearchBox client:load /> // hydrate immediately (above the fold)
<Carousel client:visible /> // hydrate when scrolled into view
<NewsletterForm client:idle /> // hydrate when the main thread is idle
Islands vs RSC — a likely follow-up. They share the goal (ship less JS) but differ: islands are about where interactivity lives on the page (independent client mini-apps in static HTML, often framework-agnostic); RSC is about where a component renders (server vs client) within one React tree. RSC gives you streaming + server data access; islands give you a hard static/interactive split and easy mixing of frameworks. They're increasingly combined. [patterns.dev]
client:load for the search box, client:visible for the footer carousel — never the reverse.
“Islands flip the default: the page is static by default, interactive by exception. Each island hydrates on its own trigger — load, idle, or visible — so most of the page ships no JS at all.”
All of §3 still hydrates — just less, or later. Resumability (Qwik) is a different paradigm: don't hydrate at all. The server serializes not just the HTML but the app's entire state and the event listeners' locations into the HTML itself. The client boots with almost no JS; when you actually click something, Qwik lazy-loads just that handler and resumes execution exactly where the server left off — no re-running of components to “rediscover” the app. [Qwik]
| Hydration (React/Vue/etc.) | Resumability (Qwik) | |
|---|---|---|
| On load | download bundle, re-execute tree, attach all listeners | attach one global listener; do nothing else |
| Cost scales with | app size — O(n) in components | ~constant — O(1), regardless of app size |
| JS at startup | the whole component bundle | ~0 — code loads on first interaction |
The headline: hydration's startup cost grows with the size of your app; resumability's stays roughly flat. For a huge app on a cheap phone, that's the difference between a janky and an instant TTI. The mechanism is event delegation + serialized state: the listener is already described in the HTML, so there's nothing to “wire up.”
“Hydration's startup cost grows with the app; resumability's is flat. Qwik serializes the listeners into the HTML and resumes on click instead of replaying the whole app — O(1) startup, not O(n).”
The bottleneck is real: image- and content-heavy pages, served globally to low-end devices on slow networks in emerging markets — precisely where hydration's CPU cost bites hardest and tanks INP. So the hydration answer isn't academic; it's a conversion lever. How a Lead reasons about it:
"use client" subtrees; let the rest stay static, zero-JS. This is the highest-leverage move and works in today's React stack.client:load for the above-the-fold search bar; client:visible for the below-the-fold carousel/reviews — don't pay for off-screen interactivity up front.Concept: hydration is SSR's client-side bill; I pay it down by hydrating less (islands/RSC) and later (lazy/progressive), and I know resumability removes it entirely. Trade-off: deferring hydration risks “interactive later”; resumability buys O(1) startup but costs serialization and an ecosystem switch. Anchor: “We had fast FCP but poor INP on low-end devices; we moved the listing to mostly-static with hydrated islands for filters/map and lazy-hydrated the below-fold — INP dropped without losing SEO.” Impact: in our markets, startup responsiveness is conversion, so cutting hydration CPU is a business metric, not a vanity one. Invite: “If we were greenfield and INP-bound on cheap hardware, I'd seriously prototype a resumable framework; in our React monorepo, islands + RSC get most of the win without the rewrite.”
Pick an answer; instant feedback. Push-back style, like the round.
1. In one sentence: what is hydration, and why does SSR need it?
2. Why is hydration expensive — name the properties that make it hurt.
3. The "cruel irony" of SSR + full hydration on low-end devices is…
4. Map the three "levers" to strategies: hydrate later / hydrate less / hydrate never.
5. Describe the islands architecture mental model.
6. In Astro, you have an above-the-fold search box and a below-the-fold image carousel. Which directives?
scn: minimise startup JS without making the primary action laggy.
7. Interviewer: "Islands and RSC — same thing?" Sharpest distinction?
8. How is resumability fundamentally different from all the hydration strategies?
9. The headline performance claim for resumability vs hydration is about scaling. State it.
10. the listing page: fast FCP but poor INP on low-end devices in emerging markets. Highest-leverage fix in a React stack today?
11. When is reaching for a resumable framework (Qwik) the right call — and how do you frame it as a Lead?
0 / 11 answered