Reducing JavaScript Hydration Cost: Partial Hydration and Resumability
AI generated
60fps
ms
Web Performance · JavaScript · SSR · Frontend Architecture
Reducing JavaScript hydration cost
partial hydration and resumability compared

Server-side rendering is supposed to deliver fast first visibility, yet in large SPA/SSR hybrid applications the hydration step that follows often eats up exactly the advantage server-side rendering just created. The browser has to walk the entire component tree again, attach event handlers, and reconstruct internal state before the page becomes truly interactive. Partial hydration and the fundamentally different concept of resumability are two answers to the same problem, with very different consequences for architecture and framework choice.

16 min read Hydration · SSR · Islands architecture Resumability · Qwik · Partial hydration

1. Why hydration becomes a bottleneck in large SPA/SSR hybrids

Server-side rendering (SSR) promises fast initial display because the server ships already finished HTML that the browser can show right away, without waiting for JavaScript to load and run. That promise only covers the visual display though, the First Contentful Paint. For the page to actually respond to clicks, input, and scroll events, the client-side JavaScript code has to rebuild the same component structure, reconcile it against the already present DOM, and attach event listeners, a process known as hydration.

On small pages this process barely registers, but on large single-page applications with hundreds of components, hydration itself becomes the most expensive phase of the load sequence. The browser has to walk the entire component tree, even for sections that will never become interactive, such as static blog text or a legal footer. The result is a paradoxical situation where the page looks visually finished, yet every user interaction falls flat because JavaScript execution is blocking the main thread. This phenomenon is known as the 'uncanny valley' of web performance.

2. What actually happens during hydration, technically

Technically, hydration runs through several steps: first the browser downloads the application's JavaScript bundle, parses, and compiles it. Then the framework, React or Vue for instance, runs the same rendering pass that already happened on the server, producing a virtual component tree in the browser's memory. That virtual tree gets reconciled against the actual existing DOM to make sure both match, before event handlers finally get attached to the corresponding DOM nodes.

The critical point is that this entire process runs synchronously and mostly on the main thread, putting it in direct competition with other important work such as processing user input. On an application with many nested components, the raw JavaScript execution time for hydration can reach several seconds on an average mobile device, while the same process is barely noticeable on a powerful desktop machine. That gap explains why lab measurements on developer hardware routinely paint a far too optimistic picture of the real user experience.


// Classic full hydration (React, simplified)
// The ENTIRE tree gets hydrated, including purely static sections
import { hydrateRoot } from 'react-dom/client';
import App from './App';

// App contains e.g. header, footer, blog text (static)
// and a single interactive add-to-cart button
hydrateRoot(document.getElementById('root'), <App />);

// Problem: header, footer, and blog text all get hydrated in
// full even though they never respond to events -- pure wasted
// compute time on the main thread.

3. Measuring hydration cost: TBT and INP

To evaluate hydration cost objectively, looking at raw load time is not enough, because hydration primarily affects interactivity. Total Blocking Time (TBT) measures how long the main thread is blocked by long tasks, while Interaction to Next Paint (INP), a Core Web Vital, measures how responsive a page is to actual user interactions across its entire lifespan. Both metrics react sharply to expensive hydration, because the main thread is blocked to input while hydration runs.

In practice the problem often surfaces only under realistic use: a user sees a page that looks finished, clicks a button that is visually present, but whose event handler has not been attached yet because that component's hydration happens later in the bundle loading process. Such clicks either get lost entirely or get processed with a noticeable delay afterward, which user studies find especially frustrating, since the page visually suggests an interactivity that technically does not yet exist.

4. Partial or selective hydration as a solution

Partial hydration, sometimes called selective hydration, challenges the core assumption of classic hydration: not every component on a page actually needs client-side JavaScript. Static blog text, a footer full of links, or a plain product description contain no interactivity whatsoever and therefore never need to be hydrated. Only components that genuinely need to respond to user input, a shopping cart widget, a filter form, or an image carousel, receive client-side JavaScript and get hydrated individually.

Frameworks like Astro turn this principle into a central architectural concept with explicit directives such as client:visible or client:idle, where each component decides for itself whether and when it gets hydrated. client:visible delays hydration until the component actually scrolls into the viewport, while client:idle defers it until the main thread is free. This granular approach often cuts initial JavaScript execution time by more than eighty percent compared to classic full hydration, because most of a typical content page is static anyway.

5. Islands architecture in practice

The architectural concept behind partial hydration is often called islands architecture: the page consists of a static HTML ocean in which individual interactive components are embedded like islands, each with its own independent hydration cycle. These islands do not necessarily communicate directly with each other; instead they use URL parameters, custom events, or a minimal shared store, which deliberately keeps coupling between components low and makes it easier to optimize each island on its own.

The practical benefit shows up especially on content-heavy pages like blogs, documentation portals, or e-commerce category pages, where most of the content is purely informational and only a few clearly scoped areas need real interactivity. Frameworks like Astro, Fresh, or even React Server Components with selective client components implement this pattern in different flavors, but the underlying idea stays the same: JavaScript is shipped and executed only where it is actually needed.

6. Resumability as a fundamentally different concept

While partial hydration narrows down the problem, resumability, as implemented in the Qwik framework, takes a radically different approach: it tries to avoid hydration as a concept entirely. Instead of rebuilding the component tree and internal state in the browser after load, Qwik serializes the complete application state, including event listener references, directly into the shipped HTML. The browser does not need to recompute that state on load; it can 'resume' it on demand, hence the name resumability.

Concretely, this means that when a user clicks a button, the browser at that exact moment loads only the minimal JavaScript code needed for that one interaction, instead of loading and running the entire framework and all event handlers upfront. This symbol-level lazy-loading granularity means initial JavaScript execution time on page load trends toward zero, regardless of how complex the application actually is, because no upfront hydration step is required anymore.

7. Hydration versus resumability, side by side

The central difference can be summarized like this: partial hydration reduces the amount of work that needs to be hydrated by skipping irrelevant sections entirely, but for the remaining interactive components, the classic hydration mechanism (load, execute, reconcile) still applies. Resumability, on the other hand, eliminates the entire hydration concept for the whole application and replaces it with demand-driven, fine-grained code loading triggered by the first actual interaction event.

This fundamental difference comes at a price: resumability requires a completely redesigned framework with its own compiler, its own serialization format, and an ecosystem that is still considerably smaller than the established React or Vue landscape. Partial hydration, by contrast, can often be introduced into existing frameworks and projects incrementally, without swapping out the whole technical foundation, which in practice makes it the more pragmatic choice for existing, grown codebases.

8. Practical reduction strategies for existing applications

For teams that cannot or do not want to switch frameworks right away, there are still practical approaches available inside classic SSR frameworks like Next.js or Nuxt. Lazy-loading components through dynamic imports delays hydration of non-critical sections until they are actually needed, while React Server Components allow components to be rendered fully on the server and never shipped to the client at all when they contain no interactivity whatsoever.

Other effective measures include deliberately splitting large JavaScript bundles by route, so users only load the code for the page they are currently visiting, and consciously trimming state management down to what is actually necessary, since large global stores slow down reconciliation during hydration even further. An audit with the Chrome performance panel, explicitly filtered for long tasks during the hydration phase, usually reveals quickly which components account for the largest share of blocking time.

9. Conclusion: which approach fits which project

For new projects with mostly static, informational content and few interactive areas, partial hydration through frameworks like Astro is usually the most pragmatic, lowest-risk path, since it can be combined with an already mature ecosystem. For highly interactive applications, where interactivity plays a central role on practically every page, a closer look at resumability is worthwhile, even though switching to a new framework like Qwik comes with corresponding migration effort.

Regardless of the approach chosen, one thing holds true: hydration cost cannot be ignored once an application grows past a certain size, because it directly affects perceived responsiveness and therefore the Core Web Vitals. Regularly measuring TBT and INP on realistic mobile hardware, not just on developer laptops, should be a fixed part of every performance review before committing to an architectural decision for or against a particular hydration approach.

Approach Hydration scope Framework examples Migration effort
Classic full hydration Entire component tree React (classic), Vue (classic) none (default behavior)
Partial/selective hydration Only marked interactive components Astro, Fresh, Qwik-adjacent low to medium
Islands architecture Isolated, independent islands Astro, Marko medium (architectural shift)
Resumability No classic hydration concept Qwik high (framework switch)

Mironsoft

Web performance, Core Web Vitals, and load time optimization

Load times that don't make users bounce before the page is even visible?

We review existing websites for slow Core Web Vitals, bloated JavaScript bundles, and unnecessary render blockers, then build a performance foundation that stays measurable instead of just looking good once.

Performance Audit

Systematically measuring and fixing Core Web Vitals, load waterfall, and render blockers.

Bundle Optimization

Specifically reducing JavaScript and CSS bundle size and improving code splitting.

Monitoring Setup

Establishing continuous performance monitoring instead of a one-time snapshot.

10. Summary

JavaScript hydration cost at a glance

Core problem

The main thread gets blocked during hydration, even for static sections that never become interactive.

Pragmatic fix

Partial hydration selectively hydrates only the components that are actually interactive.

Radical alternative

Resumability (Qwik) eliminates hydration entirely in favor of demand-driven code loading.

Key metrics

Total Blocking Time and Interaction to Next Paint objectively expose hydration cost.

11. FAQ: JavaScript hydration cost at a glance

1What is hydration in the context of SSR applications?
Hydration is the process by which client-side JavaScript walks through a page already delivered as HTML by the server, rebuilds the component tree in the browser, and attaches event handlers to the existing DOM elements so the page becomes interactive.
2Why is hydration especially expensive on large applications?
Because the browser has to walk the entire component tree, even for sections that never become interactive. With hundreds of components, this work adds up to several seconds of main-thread blocking time on average mobile hardware.
3What is the difference between partial and full hydration?
Full hydration hydrates every component on the page regardless of whether it is interactive. Partial hydration only hydrates the components that actually need to respond to user input and skips static sections entirely.
4What does islands architecture mean?
Islands architecture describes a page structure where static HTML makes up most of the page and individual interactive components are embedded like independent islands, each with its own isolated hydration cycle.
5What is resumability and how does it differ from hydration?
Resumability, implemented in the Qwik framework, serializes the complete application state, including event listener references, directly into the HTML. Instead of recomputing state after load, only the minimal code needed for a specific interaction gets loaded on demand.
6Which metrics reveal hydration cost?
Total Blocking Time (TBT) measures how long the main thread is blocked by long tasks, while Interaction to Next Paint (INP), a Core Web Vital, measures responsiveness to actual user interactions across the page's entire lifespan.
7Is switching to Qwik worth it purely because of hydration cost?
That depends heavily on the project. For highly interactive applications, switching can bring significant performance gains, but it requires a newly designed compiler and serialization system as well as a smaller ecosystem compared to React or Vue.
8Can I introduce partial hydration without switching frameworks?
Yes, through lazy-loading components via dynamic imports and, in React applications, through React Server Components, many of the benefits of partial hydration can be achieved incrementally in existing projects too.
9How do I identify which components cause the biggest hydration load?
The Chrome performance panel explicitly shows long tasks during the hydration phase. Filtering on that time range usually quickly reveals which components account for the largest share of blocking time.
10Is partial hydration worthwhile for every type of website?
It is especially well suited to content-heavy pages with few interactive areas, such as blogs or documentation portals. For applications where practically every component is interactive, such as complex dashboards, the benefit is smaller and a different optimization approach is often more suitable.