Hydration is one of the most important concepts in modern frontend frameworks, yet also one of the most misunderstood.
In 2025, frameworks like React 18+, Vue/Nuxt 3, SvelteKit, SolidStart, Qwik, Astro all approach hydration differently — some progressively, some selectively, some partially, some never fully.
This post breaks it all down with real examples, SSR + SSG scenarios, and an extended example so the concept stays in your mind forever.
🌊 What Is Hydration?
When a page is SSR or SSG, the browser gets plain HTML:
<div id="app"> <button>Click me</button> <ul> <li>Item 1</li> <li>Item 2</li> </ul> </div> But HTML alone does nothing — no JavaScript, no event listeners.
Hydration is when the framework:
- Loads JS bundles
- Recreates the virtual component tree
- Attaches event listeners
- Activates reactivity
- Makes the page interactive
Another way to say it:
SSR/SSG gives HTML.
Hydration gives life.
🟥 Old Hydration Model (Pre-React 18 / Classic Vue / SvelteKit)
Traditionally, hydration happened linearly, top-to-bottom:
1. Hydrate App() 2. Hydrate Header() 3. Hydrate Navbar() 4. Hydrate Sections() 5. Hydrate Footer() Linear hydration has three big problems:
❌ 1. Slow Time-To-Interactive
Because hydration must finish before the user can interact safely.
❌ 2. Big JS bundle runs all at once
Blocks main thread.
❌ 3. Offscreen components hydrate early
Even things not visible yet.
This model works, but it’s outdated for large apps.
🟩 React 18+: Selective Hydration (Smarter, Not Linear)
React 18 replaced “hydrate everything in order” with:
Hydrate what’s needed, when it’s needed.
Skip or delay the rest.
React evaluates multiple signals:
React 18 automatically detects:
- Which components are visible
- Which components are interactive
- Which have pending Suspense data
- Which are above-the-fold
- Which the user interacts with first
- Which are heavier or lower priority
Hydration priority:
1️⃣ Visible + interactive components
2️⃣ User-clicked or user-focused components
3️⃣ Above-the-fold content
4️⃣ Suspense boundaries that resolved
5️⃣ Below-the-fold or non-interactive UI
6️⃣ Everything else, when browser is idle
This is selective hydration — only hydrate the parts users need immediately.
🟦 How Suspense Affects Hydration
Consider:
<Suspense fallback={<LoadingUser />}> <UserProfile /> </Suspense> React can stream HTML in chunks:
- Server streams
<LoadingUser />first - Later streams
<UserProfile />when data resolves - Client hydrates these boundaries independently
- Hydration can happen in the middle, out of order, or scattered
Suspense creates natural hydration islands.
🔥 BIG Real-World Example (SSR + Suspense + Complex Layout)
Imagine a dashboard:
+----------------------+ | Navbar | +----------------------+ | Sidebar | Chart | | | (Suspense) | | | | +----------------------+ Server Output (HTML)
- Navbar rendered immediately
- Sidebar rendered immediately
- Chart is slow → Suspense fallback is sent
Client sees:
Navbar (interactive instantly) Sidebar (interactive instantly) Chart loading skeleton… Hydration (React 18):
1️⃣ User clicks sidebar → React hydrates sidebar immediately
2️⃣ Navbar is visible → hydrate early
3️⃣ Chart fallback hydrates next (lightweight)
4️⃣ Actual Chart component hydrates only when its data resolves
5️⃣ Footer hydrates when browser is idle
This is selective, scattered, priority-driven hydration.
🟨 Is Progressive Hydration Needed in React?
React 18 already does a form of progressive hydration:
✔ Selective hydration
✔ Suspense boundary hydration
✔ Hydrate-on-interaction
✔ Hydrate-visible-first
✔ Hydrate background content later
✔ Hydrate streaming chunks as they arrive
React does not call it “progressive hydration,” but practically, it achieves that behavior.
🟩 SSG + Hydration — Does Hydration Still Happen?
Yes.
SSG also needs hydration because:
- HTML is static
- But JS must still attach event handlers
- Framework re-runs components on client
- Event listeners get connected
- State becomes reactive
Example:
A static blog page still hydrates “Like Button”, comments, interactive parts.
SSG = static HTML + hydration on client.
🔰 How Other Frameworks Handle Progressive Hydration
Now let's compare how different frameworks hydrate:
🟦 Vue 3 (Classic) — Linear Hydration
Vue's default hydration is still linear unless using advanced tooling.
Hydration flow:
1. Hydrate root instance 2. Hydrate children in DOM order 3. Hydrate nested components 4. Finally hydrate leaf nodes Downsides:
- Slower interactivity for large pages
- No Suspense-driven hydration
- No hydrate-on-interaction by default
But this changes with Nuxt 3…
🟩 Nuxt 3 — “Progressive, Island-Style” Hydration
Nuxt 3 introduced partial hydration capabilities, similar to islands architecture:
✔ client:only
Component is only hydrated when needed:
<client-only> <FancyChart /> </client-only> ✔ Async Components + Suspense
Nuxt 3 supports Suspense-like behavior:
<Suspense> <UserProfile /> </Suspense> This allows:
- above-the-fold hydration first
- async component hydration later
- streamed HTML + partial hydration
✔ Nuxt + Nitro + Island Optimizations
Nuxt aggressively optimizes hydration for:
- per-component hydration
- hydration skipping for static parts
- “hybrid island architecture”
While it's not as automatic as React's selective hydration,
Nuxt 3 can achieve progressive hydration with explicit patterns.
🟧 SvelteKit — Progressive Hydration by Default
SvelteKit hydrates:
- visible sections first
- interactive sections first
- lazy components when needed
Svelte also supports component-level hydration:
- preload only what's visible
- lazy load everything else
- hydrate only when scrolled into view
- hydrate on interaction
Very efficient because Svelte compiles away most framework runtime.
🟦 SolidStart — Fine-Grained Hydration
Solid uses reactive primitives and compiles away components.
Hydration happens:
- per DOM node
- per reactive signal
- not per component
This makes hydration extremely granular.
SolidStart naturally does progressive + selective hydration.
🟣 Qwik — No Hydration (Resumes Instead)
Qwik doesn’t hydrate at all.
It resumes the app from HTML with embedded state.
Event listeners are attached lazily.
Progressive hydration is built-in because Qwik doesn't hydrate entire trees — it loads behavior on demand.
🌑 Astro — Island Architecture (Hydrate Only What You Ask)
Astro hydrates components only when instructed:
<Counter client:load /> <Chart client:visible /> <Sidebar client:idle /> Modes:
client:loadclient:visibleclient:idleclient:mediaclient:hover
A pure island architecture.
Astro minimizes JS for non-interactive content, best-in-class for performance.
🟩 Summary: How Frameworks Hydrate
| Framework | Hydration Style | Notes |
|---|---|---|
| React 18+ | Selective, priority-based, Suspense-aware | Automatic |
| Vue 3 classic | Linear | Simple but slower |
| Nuxt 3 | Partial/Client-only/Islands | Supports progressive hydration |
| SvelteKit | Progressive | Hydrates visible/interacted UI first |
| SolidStart | Fine-grained | Tiny hydration chunks |
| Qwik | No hydration → Resume | Most advanced |
| Astro | Islands | Hydrate only what you choose |
🎉 Final Thoughts
In modern SSR/SSG frameworks:
- Hydration is the cost of interactivity
- Linear hydration is slow
- Progressive + selective hydration is the future
- React 18+ achieves it automatically
- Nuxt 3, SvelteKit, Qwik, Astro each do it differently
- Suspense brings async-shaped hydration
- Streaming SSR improves TTFB dramatically
If you understand hydration deeply,
you understand the foundation of modern web frameworks in 2025.
Top comments (0)