Selective Hydration — Hydrating Only Interactive Components
In this tutorial, you will learn about Selective Hydration. We cover key concepts, practical examples, and best practices to help you master this topic.
Selective hydration hydrates only the interactive parts of server-rendered pages, keeping static sections as pure HTML for minimal JavaScript and faster interactivity.
What You'll Learn
By the end of this tutorial, you will understand what selective hydration is, how React 18 supports it through Suspense and useId, how to implement selective hydration patterns, and how it reduces JavaScript bundle size by keeping static content unhydrated.
Why It Matters
Most of a page is static content that does not need JavaScript. Traditional SSR hydrates everything, wasting bandwidth and CPU on content that will never be interactive. Selective hydration only hydrates components that actually need interactivity, dramatically reducing the JavaScript sent to the browser.
Real-World Use
A documentation site with hundreds of pages used selective hydration. The main content (mostly markdown) was server-rendered and never hydrated. Only the search bar, navigation toggle, and copy-code buttons were interactive. JavaScript bundle size dropped from 240KB to 45KB. Pages loaded in under 1 second on 3G connections.
Selective Hydration Architecture
┌──────────────────────────────────────────────────────────┐
│ Selective Hydration │
├──────────────────────────────────────────────────────────┤
│ │
│ Server-Rendered (static HTML, no JS): │
│ ┌────────────────────────────────────────────────┐ │
│ │ Article content (h1, p, ul, code blocks) │ │
│ │ Author bio, Related articles │ │
│ │ Footer links, Copyright │ │
│ │ (These never hydrate — 0 KB JavaScript) │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ Selectively Hydrated (client JS attached): │
│ ┌────────────────────────────────────────────────┐ │
│ │ Search bar (onChange, onSubmit) │ │
│ │ Mobile menu toggle (onClick) │ │
│ │ Copy code button (onClick, clipboard API) │ │
│ │ Theme switcher (onClick, localStorage) │ │
│ │ (Small JS chunks, only for interactive elems) │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ Result: Minimal JavaScript, fast interactivity │
│ │
└──────────────────────────────────────────────────────────┘
Think of selective hydration like a museum with both static exhibits and interactive kiosks. The paintings and sculptures (static HTML) do not need electricity — they are perfectly fine as they are. The interactive kiosks (buttons, search) need power (JavaScript) to function. Selective hydration only runs power to the kiosks, not to every painting in the museum.
Separating Interactive from Static Content
// Static Server Component — never sends JavaScript
function ArticleContent({ article }) {
return (
<article>
<h1>{article.title}</h1>
<div className="content">
{article.paragraphs.map((p, i) => (
<p key={i}>{p}</p>
))}
</div>
<div className="author-bio">
<p>Written by {article.author}</p>
</div>
</article>
);
}
// Interactive component — client JavaScript needed
'use client';
function CopyCodeButton({ code }) {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
await navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<button
onClick={handleCopy}
className="copy-btn"
aria-label="Copy code to clipboard"
>
{copied ? 'Copied!' : 'Copy'}
</button>
);
}
// Parent — selective hydration
function BlogPost({ article }) {
return (
<div>
{/* This component is pure HTML — no JavaScript */}
<ArticleContent article={article} />
{/* This component hydrates — JavaScript sent to client */}
<CopyCodeButton code={article.code} />
</div>
);
}
When to Use Selective Hydration
// Components that NEED hydration (interactive):
const interactiveComponents = [
'Forms with validation',
'Buttons with onClick handlers',
'Search boxes with autocomplete',
'Modals and dialog boxes',
'Dropdown menus',
'Tab switchers',
'Accordion/collapse widgets',
'Carousels and sliders',
'Toast notifications',
'Real-time data (counters, timers)'
];
// Components that DO NOT need hydration (static):
const staticComponents = [
'Article and blog content',
'Product descriptions',
'User profiles (read-only)',
'Navigation links',
'Footer content',
'Headers and titles',
'Images and captions',
'Tables of data (read-only)',
'Lists and bullet points',
'Static SVG illustrations'
];
// Decision helper
function needsHydration(component) {
// Does the component respond to user input?
// Does it have state that changes after initial render?
// Does it use browser-only APIs (localStorage, clipboard)?
// Does it communicate with a server (fetch, WebSocket)?
// If NO to all: it does not need hydration
return component.usesState ||
component.usesEffects ||
component.hasEventHandlers ||
component.usesBrowserAPIs;
}
Selective Hydration with Islands
// Islands architecture pattern
// Static HTML with islands of interactivity
// Page component — mostly static HTML
function ProductPage({ product }) {
return (
<div>
{/* Static section — no JS needed */}
<ProductImages images={product.images} />
{/* Interactive island */}
<Island>
<AddToCartButton productId={product.id} />
</Island>
{/* Static section */}
<ProductDetails product={product} />
{/* Interactive island */}
<Island>
<ReviewForm productId={product.id} />
</Island>
{/* Static section */}
<RelatedProducts products={product.related} />
</div>
);
}
// Island component — marks interactive boundaries
function Island({ children }) {
return (
<div data-island>
{children}
</div>
);
}
// Framework like Astro or Marko handles this automatically
// The framework detects islands and only sends JS for those components
// Static components are never hydrated — pure HTML
Common Mistakes
- Making everything interactive by default. It is easier to make components interactive than to decide which need it. But every interactive component adds JavaScript. Default to static, opt in to interactive.
- Hydrating interactive islands too early. Even interactive components can defer non-critical initialization. Only hydrate what the user can immediately interact with.
- Not measuring the static-to-interactive ratio. Aim for at least 60 percent of your page being static (no JS). Measure the JavaScript sent for each component.
- Breaking layout with hydration islands. Interactive islands must not change the layout when they hydrate. Use fixed dimensions and placeholder sizes.
- Forgetting SEO meta tags in interactive components. Meta tags in components that only render on the client (not SSR) will not be seen by search engines.
Practice Questions
- What is the difference between selective hydration and progressive hydration?
- Which components should be selectively hydrated vs kept static?
- How do you measure which components need JavaScript?
- What is the islands architecture and how does it relate to selective hydration?
- How do you ensure static content that later becomes interactive does not break layout?
Challenge: Build a product page and identify which components need hydration and which can remain static. Separate them into two groups. Implement selective hydration so that only the Add to Cart button, review form, and search bar send JavaScript. Measure the JavaScript bundle size saved compared to hydrating everything.
FAQ
Mini Project
Build a blog with selective hydration: the article body is static HTML (no React, no hydration), the search bar is an interactive island (React with hydration), the comments section is another island, and the theme switcher is a small interactive widget. Use separate React roots for each island. Measure JavaScript sent to the client.
What's Next
You understand selective hydration. Now explore Partial Hydration for mixing static and interactive content.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro