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)