Hydration Strategies Compared: Full, Partial, Streaming

Hydration is the step where client JavaScript attaches behavior to server-rendered HTML. The content a crawler indexes is the same in every strategy — it comes from the server render — but how you hydrate determines Total Blocking Time, time-to-interactive, and layout stability, all of which feed Core Web Vitals and therefore ranking. This guide compares the three approaches and complements the streaming SSR cluster.

Full versus partial versus progressive hydration Full hydration boots all components at once; partial hydration boots only interactive islands; progressive hydration boots components lazily over time. Full hydrate entire tree on load — highest blocking time Partial island island rest stays static HTML Progressive on idle on viewport on interaction
All three index the same server HTML; they differ in how much JavaScript runs, and when.

Choosing a strategy, step by step

  1. Default to partial hydration for content sites. Mark only genuinely interactive components as islands; everything else ships zero JavaScript and stays indexable as static HTML.

    // ✅ Astro-style island: only this component hydrates
    <SearchBox client:idle />
    <ArticleBody />   {/* static HTML, no JS shipped */}
  2. Use progressive hydration for large interactive trees. Defer hydration of below-the-fold widgets until idle or viewport entry to free the main thread.

    // ✅ Hydrate on viewport entry instead of on load
    const Comments = lazyHydrate(() => import('./Comments'), { on: 'visible' });
  3. Avoid full hydration on content-heavy pages. Booting the whole tree on load is the simplest path but the worst for Total Blocking Time.

    // ❌ Hydrates everything immediately — blocks the main thread on load
    hydrateRoot(document.getElementById('root'), <EntireApp />);
  4. Reserve layout space for hydrated widgets so mounting does not shift content — see reducing layout shift during hydration.

Total Blocking Time by hydration strategy Illustrative Total Blocking Time falls sharply from full hydration to partial and progressive hydration. Total Blocking Time by strategy (illustrative) 600 ms 300 ms 0 ms 580 ms 210 ms 130 ms Full Partial Progressive
Booting the whole tree on load dominates Total Blocking Time; hydrating only islands or deferring work cuts main-thread blocking by more than half.

Validation

  • Lighthouse Total Blocking Time drops when you move from full to partial/progressive hydration.
  • Performance panel shows shorter main-thread long tasks after load.
  • GSC URL Inspection rendered HTML is unchanged — confirming hydration strategy does not alter indexable content.
  • CLS field data stays under 0.1 with reserved layout space.
Main-thread occupancy: one long task versus many short ones Full hydration runs one long blocking task while progressive hydration spreads several short tasks across idle, viewport, and interaction, keeping input responsive. One long task versus many short ones Full one long task blocks input Progressive short tasks spread across idle, viewport entry, and interaction — time →
Full hydration parks a single long task on the main thread; progressive hydration breaks it into short, deferred tasks so the page stays responsive to input.

Configuration reference

// Progressive hydration helper: import + hydrate a component lazily
function lazyHydrate(importFn, { on }) {
  return function Hydrated(props) {
    const ref = useRef(null);
    useEffect(() => {
      const trigger = () => importFn().then(({ default: C }) =>
        hydrateRoot(ref.current, <C {...props} />));
      if (on === 'idle') requestIdleCallback(trigger);
      if (on === 'visible') new IntersectionObserver((e, o) => {
        if (e[0].isIntersecting) { trigger(); o.disconnect(); }
      }).observe(ref.current);
    }, []);
    return <div ref={ref} />;
  };
}
How each on option maps to a browser API and hydration moment The idle option uses requestIdleCallback to hydrate when the main thread is free, visible uses IntersectionObserver to hydrate on viewport entry, and interaction uses an event listener to hydrate on first user input. What each on value binds to, and when it fires on: browser API hydrates when 'idle' requestIdleCallback the main thread is free 'visible' IntersectionObserver the ref enters the viewport 'interaction' event listener the first user input
The helper's on option is just a switch over browser primitives — each value picks the API that decides when hydration runs.

Frequently Asked Questions

Does hydration affect SEO? Hydration does not change the server-rendered HTML a crawler indexes, but it affects Core Web Vitals — heavy full-page hydration raises Total Blocking Time and can cause layout shift, both of which influence ranking. Partial and progressive hydration reduce that cost.

What is progressive hydration? Progressive hydration hydrates components lazily — on viewport entry, interaction, or idle time — instead of all at once on load. It keeps the main thread free so the page is interactive sooner without changing what crawlers index.

← Back to CSR, SSG, SSR & ISR Rendering Strategies