Skip to content

Partial Hydration — Mixing Static and Interactive Content in SSR

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Partial Hydration. We cover key concepts, practical examples, and best practices to help you master this topic.

Partial hydration renders most of the page as static HTML and only adds JavaScript interactivity to isolated components, reducing bundle size and improving performance.

What You'll Learn

By the end of this tutorial, you will understand what partial hydration is, how it differs from full hydration, the islands architecture pattern, how frameworks like Astro and Marko implement partial hydration, and how to build pages with partial hydration manually.

Why It Matters

On average, less than 30 percent of a page needs JavaScript interactivity. Full hydration sends JavaScript for the entire page, most of which is never used. Partial hydration sends JavaScript only for interactive islands, resulting in 50-80 percent smaller bundles, faster loads, and better performance on slow devices.

Real-World Use

Astro, a framework built on partial hydration, powers the website of a major SaaS company. Their landing page has 12 interactive components (navigation, forms, animations) out of 50+ total components. With partial hydration, they send only 35KB of JavaScript instead of 180KB. The page scores 98 on Lighthouse performance.

Partial Hydration vs Full Hydration
    ┌──────────────────────────────────────────────────────────┐
    │    Full Hydration        vs        Partial Hydration     │
    ├──────────────────────────────────────────────────────────┤
    │                                                          │
    │  All components become     Only interactive islands     │
    │  hydrated (JS attached)    become hydrated              │
    │                                                          │
    │  ┌──────────────────┐     ┌──────────────────────┐      │
    │  │ Header   ████    │     │ Header (static)      │      │
    │  │ Nav      ████    │     │ Nav (static)         │      │
    │  │ Hero     ████    │     │ Hero (static)        │      │
    │  │ Form     ████    │     │ Form (██ interactive)│      │
    │  │ Cards    ████    │     │ Cards (static)       │      │
    │  │ Footer   ████    │     │ Footer (static)      │      │
    │  └──────────────────┘     └──────────────────────┘      │
    │                                                          │
    │  JS: 200KB for all       JS: 35KB for interactivity    │
    │  TTI: 4.2s               TTI: 1.5s                      │
    │                                                          │
    └──────────────────────────────────────────────────────────┘

Think of partial hydration like a coloring book. The entire page is printed in black and white (static HTML). Most of the page stays in black and white — it is perfectly readable and useful. Only specific elements get color (JavaScript hydration) — the parts that need it. Coloring every part of the book would waste time and crayons.

Implementing Partial Hydration with Astro

// Astro component — most of the page is static
---
// This code runs at build time or on the server
import Header from '../components/Header.astro';
import HeroSection from '../components/HeroSection.astro';
import ProductGrid from '../components/ProductGrid.astro';
import AddToCartButton from '../components/AddToCartButton.jsx';
import NewsletterForm from '../components/NewsletterForm.jsx';
---

<html>
<body>
    <!-- Static components  no JavaScript -->
    <Header />
    <HeroSection />
    <ProductGrid products={products} />

    <!-- Interactive islands  JavaScript sent to client -->
    <!-- client:load  load and hydrate immediately -->
    <AddToCartButton
        productId={product.id}
        client:load
    />

    <!-- client:idle  hydrate when browser is idle -->
    <NewsletterForm client:idle />

    <!-- client:visible  hydrate when visible in viewport -->
    <LiveChat client:visible />

    <!-- client:media  hydrate only on desktop -->
    <DashboardWidget client:media="(min-width: 768px)" />

    <!-- client:only  client-side only, no SSR -->
    <UserPreferences client:only="react" />

    <!-- Static footer -->
    <footer>...</footer>
</body>
</html>

Manual Partial Hydration

// Manual partial hydration without a framework
// 1. Server renders full HTML
// 2. Identify interactive sections with data attributes
// 3. Manually hydrate only those sections

// Server output:
// <div id="root">
//     <header>Static header</header>
//     <main>
//         <p>Static content — no JS needed</p>
//         <div class="island" data-component="SearchForm">
//             <form>
//                 <input type="search" />
//                 <button>Search</button>
//             </form>
//         </div>
//         <p>More static content</p>
//         <div class="island" data-component="ThemeToggle">
//             <button>Toggle Theme</button>
//         </div>
//     </main>
//     <footer>Static footer</footer>
// </div>

// Client hydration — only hydrate islands
import { createRoot, hydrateRoot } from 'react-dom/client';

const componentMap = {
    SearchForm: () => import('./SearchForm'),
    ThemeToggle: () => import('./ThemeToggle'),
};

document.addEventListener('DOMContentLoaded', () => {
    document.querySelectorAll('.island').forEach(island => {
        const componentName = island.dataset.component;
        const loader = componentMap[componentName];

        if (loader) {
            loader().then(module => {
                const Component = module.default;
                hydrateRoot(island,
                    React.createElement(Component, island.dataset)
                );
            });
        }
    });
});

Common Mistakes

  1. Making too many components interactive. Every interactive island adds JavaScript. Before adding interactivity, ask: does this component really need JavaScript, or can it work with CSS-only?
  2. Not considering SEO for interactive islands. Content inside interactive islands that render on the client may not be indexed. Ensure critical SEO content is in the static HTML.
  3. Breaking the page without JavaScript. Static content should be fully functional without JavaScript. Interactive islands are enhancements, not requirements.
  4. Inconsistent styling between static and interactive islands. CSS should be consistent whether components are static or interactive. Use utility classes or a shared stylesheet.
  5. Complex state management across islands. Islands are isolated. Sharing state between islands requires a global event system or a shared store, adding complexity.

Practice Questions

  1. What is the difference between full hydration and partial hydration?
  2. How does Astro implement partial hydration with client directives?
  3. How do you manually implement partial hydration without a framework?
  4. What type of content should remain static in partial hydration?
  5. How do you handle state sharing between hydrated islands?

Challenge: Build a landing page with Astro-style partial hydration: one interactive island (a newsletter signup form with validation), and the rest static HTML. Then build the same page with full hydration (React on the entire page). Compare JavaScript bundle size, TT I, and Lighthouse scores.

FAQ

What frameworks support partial hydration?

Astro (first-class), Marko, Qwik, and Islands Architecture patterns in various frameworks. Next.js and Nuxt do not support partial hydration natively.

Is partial hydration the same as islands architecture?

Yes. Islands architecture is the implementation pattern for partial hydration. The page is a sea of static HTML with islands of interactivity.

Can I use partial hydration with React?

Yes. Astro supports React components as interactive islands. You can also manually implement partial hydration with multiple React roots.

Does partial hydration affect SEO?

Done correctly, no. Critical content is in the static HTML and visible to crawlers. Interactive islands enhance the experience but do not contain primary content.

How much JavaScript can partial hydration save?

Typical savings are 50-80 percent. A page that sends 200KB with full hydration might send 30-60KB with partial hydration.

Mini Project

Build a blog homepage with partial hydration: use Astro (or manual implementation) with static HTML for the blog post list, author bio, and footer. Add interactive islands for: search bar (client:load), newsletter popup (client:idle), share buttons (client:visible), and dark mode toggle (client:media). Measure JavaScript budget savings.

What's Next

You understand partial hydration. Now explore Islands Architecture for building interactive components in static HTML.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro