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.
Table of Contents
- 1. Why hydration becomes a bottleneck in large SPA/SSR hybrids
- 2. What actually happens during hydration, technically
- 3. Measuring hydration cost: TBT and INP
- 4. Partial or selective hydration as a solution
- 5. Islands architecture in practice
- 6. Resumability as a fundamentally different concept
- 7. Hydration versus resumability, side by side
- 8. Practical reduction strategies for existing applications
- 9. Conclusion: which approach fits which project
- 10. Summary
- 11. FAQ
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.