Zum Inhalt springen
Core Web Vitals specialists
Performance

Server-Side Rendering and Hydration Done Right

15 min read
Server-Side RenderingHydrationIslands ArchitekturCore Web Vitals

Server-Side Rendering (SSR) is considered the default solution for fast, search-friendly websites: the server produces finished HTML that the browser can display immediately. But the second step is often underestimated. For the server-rendered page to respond to clicks and inputs, the browser must load the JavaScript code and wire interactivity onto the static HTML structure. This process is called hydration and it costs valuable processing time on the main thread. According to HTTP Archive, the median mobile page loads more than 500 KB of JavaScript (Source: Web Almanac, 2024) and encounters 14 long tasks at the median (Source: Web Almanac, 2024). This is exactly where SSR turns into either a performance advantage or a bottleneck. This article shows how to understand the cost of hydration, reduce it with islands and partial hydration, and sustainably improve your shop's Core Web Vitals.

Hydration Cost: Full Hydration vs. Islands ArchitectureServer-Side Rendering (HTML generated on the server)0s1s2s3sFull HydrationJS download (entire bundle)Hydration of the whole pageMain thread blockedinteractiveIslands / partial hydrationisland JS onlyselectiveinteractiveinteractive soonerinteractive earlierTime to Interactive80%less JS possible200msTBT target mobile8.4%more conversions per 0.1sServer renderHTML firstCritical islandshydrate by priorityLazy/on demandon interactionResumabilityno re-run

How SSR and Hydration Work Together

With server-side rendering, the server executes the application logic and generates complete HTML before the first response reaches the browser. As a result, the user sees content very early: Largest Contentful Paint (LCP) benefits because the visible markup does not have to be assembled in the browser first. In contrast, a pure single-page application (client-side rendering) initially delivers only an empty shell and a JavaScript bundle that builds the content after download. SSR shifts this work to the server and delivers a finished picture.

The catch follows immediately. The delivered HTML is static: buttons do not respond, forms do not submit, dropdowns do not open. To bring the page to life, the browser loads the same component code that already ran on the server, executes it again and connects the existing DOM elements with event handlers and the reactivity model. This process of wiring up after the fact is hydration. By default, a classic framework hydrates the entire page in one pass on the main thread, and interactivity only emerges once the complete bundle has been downloaded and executed.

The result is the so-called uncanny valley of interactivity: the page looks finished but does not yet respond. Users click a button and nothing happens because the associated event handler is not yet attached. This gap between visible content and actual usability shows up directly in Interaction to Next Paint (INP), which has measured a page's responsiveness as a Core Web Vital since March 2024. Our article on INP optimization describes complementary strategies.

SSR Does Not Equal Performance

Server-side rendering improves the first impression (paint), but it does not ensure fast interactivity. Without a thoughtful hydration strategy, an SSR page can be visible early yet usable late. The decisive lever lies in how much JavaScript must be delivered and executed for interactivity.

The Hidden Cost of Hydration

Hydration is expensive because it does the same work twice. The server has already executed the components to generate the HTML. The browser runs the same code again to rebuild component boundaries, event listeners and the reactivity graph. With classic full hydration, the browser must download, parse, compile and execute the entire JavaScript bundle before any part of the page becomes interactive. A single heavy piece of code can thus prevent the rest of the page from responding.

This work runs almost entirely on the main thread and blocks it. Any task that occupies the main thread for longer than 50 milliseconds counts as a long task; the time beyond those 50 milliseconds counts as blocking time. Total Blocking Time (TBT) sums these blocking times and correlates closely with INP. For a good rating, web.dev recommends a TBT of under 200 milliseconds on mobile devices (Source: web.dev, 2024). Hydration-heavy pages regularly breach this value because the bundled wiring happens in one large long task.

The strain becomes especially clear on mobile devices. An average mid-range smartphone processes JavaScript many times slower than a desktop machine. Since the median mobile page delivers over 500 KB of JavaScript (Source: Web Almanac, 2024) and makes a median of 22 JavaScript requests (Source: Web Almanac, 2024), long blocking phases arise precisely on the devices where a large share of shop visitors are. Anyone who ignores hydration optimizes past the real user. Our article on JavaScript performance budgets describes a systematic approach with clear upper limits.

Double Execution

The component code runs first on the server, then again in the browser. This duplication is the fundamental overhead of every classic hydration and scales with bundle size.

Main Thread Block

Hydration occupies the main thread in long tasks. During this phase the page does not respond to clicks, taps or keyboard input, which worsens INP.

Mobile Penalty

Mid-range smartphones process JavaScript considerably slower. The same hydration load leads to much longer blocking times on mobile than on desktop.

Islands Architecture: Placing Interactivity Deliberately

The islands architecture inverts the default assumption. Instead of treating the entire page as an interactive application, it views the page as predominantly static HTML with individual interactive islands. The server renders the complete page but inserts placeholders only around the truly dynamic regions, which are then hydrated into self-contained widgets in the browser. Everything else remains pure, immutable HTML that needs no JavaScript whatsoever.

The effect on JavaScript load is substantial. Since only the islands ship code, the delivered JavaScript share drops drastically. Field reports show that the islands architecture can reduce the JavaScript footprint by up to 80 percent, for example from around 1 MB to about 200 KB (Source: Web Almanac, 2024). Less JavaScript means less download, less parsing and, above all, less hydration work on the main thread. This very reduction makes strong Core Web Vitals easier to achieve.

For online shops the model is especially well suited, because most page regions are static. Product descriptions, category texts, footer, navigation and brand sections need no hydration at all. Interactivity concentrates on a few islands: the add-to-cart button, the variant selector, the quantity input and the search. A well-considered frontend optimization cleanly separates these interactive islands from the static scaffold and delivers JavaScript only where it is genuinely needed.

Page RegionInteractive?Hydration Needed?Recommendation
Product descriptionNoNoStatic HTML, no JS
Variant selectorYesYesOwn island, hydrate early
Add-to-cart buttonYesYesOwn island, hydrate early
Review carouselYesYesIsland, lazy on visibility
Footer and navigationPartlyMinimalStatic with small JS share
Chat widgetYesYesIsland, load only on click

Partial and Progressive Hydration

Partial hydration is the principle behind the islands architecture: only selected components are hydrated, the rest stays static. Progressive hydration goes a step further and controls when and in what order the islands are activated. Not every interactive component needs to respond immediately. An add-to-cart button needs early interactivity, whereas a review carousel far down the page can wait until the user even reaches it.

This yields several hydration strategies that can be set per component. A component can be hydrated immediately (for critical interactivity), when it enters the visible area (lazy on visibility), while the browser is idle, or only on the first user interaction (on demand). This fine-grained control spreads the hydration work over time instead of bundling it into a single blocking block, and keeps the main thread free for real user input.

On-demand hydration is especially effective for heavy widgets. A chat widget that is not needed on the first page view can load its code only when the user actually taps the chat button. This removes its entire hydration effort from the critical path. The technique is closely related to lazy loading and code splitting, which we cover in detail in our article on lazy loading and code splitting.

Immediate (eager)

For critical above-the-fold interactivity: add-to-cart button, variant selector, main navigation. These islands are hydrated right after loading.

On Visibility (lazy)

Components below the fold are hydrated only when they scroll into the visible area. The Intersection Observer controls the timing.

When Idle

Less important islands are hydrated when the main thread is free. The requestIdleCallback API prevents them from crowding out critical tasks.

On Demand (interaction)

Heavy widgets like chat or complex filters load their code only on the first user interaction and do not burden the initial build at all.

Streaming SSR and Selective Hydration

Modern frameworks have broken up rigid full hydration. With streaming SSR, the server no longer sends the HTML in one piece but in chunks as soon as they are ready. Slow regions, such as a personalized recommendation block, no longer hold up the entire page. The browser displays the fast parts immediately and receives the slow ones as soon as the data is available.

Selective hydration complements this streaming. Instead of waiting until the complete bundle has loaded, the framework starts hydration as soon as the code for an individual component is available, prioritizing the regions the user is currently interacting with. If someone clicks an as-yet-unhydrated component, that one is activated first. A heavy piece of code thus no longer blocks the interactivity of the rest of the page content.

This combination of streaming and prioritized hydration noticeably improves INP in practice, because the regions the user genuinely needs become interactive first. Discipline around bundle size nonetheless remains important: selective hydration distributes the work better but does not reduce the total amount of JavaScript. A thorough performance analysis reveals which components actually benefit from early hydration and which can safely wait.

Thanks to selective hydration, a heavy piece of code no longer prevents the rest of the page from becoming interactive.

React Working Group, Suspense SSR architecture

Resumability: Avoiding Hydration Entirely

The most consistent approach avoids hydration completely. Resumability serializes the application state already on the server, so the browser resumes execution where the server left off without re-executing the component code. Where hydration re-traverses the application from the root node to collect component boundaries, event listeners and the reactivity graph, resumability skips this step entirely because the server has already serialized that information.

The decisive advantage lies in scaling behavior. Classic hydration grows with page complexity: more components mean more code that must be downloaded and executed before interactivity emerges. Resumability aims for a nearly constant startup load (O(1)) because no application code runs initially. JavaScript is loaded only when a concrete interaction requires it, and even then only the part needed for it.

Resumability is still a comparatively young approach and not the right choice for every project, since it requires a suitable framework and an adjusted architectural mindset. For very large, complex shops with many interactive elements, however, it can make the decisive difference because the startup load stays low regardless of page size. In practice, the choice between partial hydration and resumability is a trade-off of maturity, team experience and concrete performance goals.

Practical Tip: Measure Hydration Before You Optimize

Use the PerformanceObserver API to capture all long tasks around the page onload event. The longest tasks shortly after the first paint usually stem from hydration. Combine this lab data with real-user monitoring of INP to see which components slow responsiveness the most, and prioritize exactly those.

An SSR Strategy for Shopware Shops

Shopware in the open-source environment relies on server-rendered HTML in the storefront with JavaScript used deliberately for interactive parts. This is a good starting point because the bulk of a product or category page is static anyway. The task is to cleanly limit the interactive JavaScript share to islands and not accidentally load the entire page with unnecessary code.

In practice, this means splitting plugin functionality into separate entry points so that, for example, a configurator or an elaborate filter module is loaded only on the pages where it is needed. Heavy third-party scripts such as chat or review widgets are loaded via an on-demand or lazy strategy. This keeps the critical path lean, and hydration concentrates on the few components the user needs immediately. An accompanying Shopware performance optimization systematically brings this separation into the storefront architecture.

The economic lever behind this is considerable. A joint study by Deloitte and Google shows that even a load-time improvement of 0.1 seconds can increase retail conversions by 8.4 percent and average order value by 9.2 percent (Source: Deloitte, 2020). Since hydration optimization shortens precisely the delay between a visible and a usable shop, it acts directly on these metrics. Accompanying measures on the server infrastructure additionally secure short response times during server-side rendering.

The Order of Optimization

First reduce the total amount of JavaScript (islands, fewer third parties), then spread the remaining hydration over time (partial, progressive), and finally activate the critical islands in a prioritized way (selective hydration). This order yields the greatest effect per effort, because less code is usually cheaper than cleverly distributed code.

Measurement and Continuous Control

SSR and hydration performance is not a one-time project. Every new feature, every plugin update and every additional interactive component can shift the carefully built balance. That is why the relevant metrics belong in continuous monitoring: Total Blocking Time and long-task count from synthetic lab tests, as well as the INP distribution from real user sessions. Only the combination of both perspectives shows the complete picture.

Synthetic tests under controlled conditions detect regressions quickly and reliably, for example when a new third-party script increases the hydration load. Real user monitoring, by contrast, shows the actual experience across the broad device landscape. Segmentation by device type is especially revealing, because mobile users with mid-range smartphones feel the hydration load many times more strongly than desktop users. This data directs optimization to the places with the greatest real-world effect.

A sustainable strategy anchors clear thresholds in the development pipeline. If the delivered amount of JavaScript or the Total Blocking Time exceeds a defined limit, the team should be notified automatically before the change reaches production. This feedback loop of measure, check, warn and fix prevents hydration load from building up unnoticed over weeks and months. Our article on JavaScript performance budgets shows how to derive concrete upper limits from this.

This article is based on data and insights from: Web Almanac by HTTP Archive (JavaScript, 2024), web.dev (Total Blocking Time, INP), Deloitte and Google (Milliseconds Make Millions, 2020), Astro Docs (Islands Architecture), React Working Group (Suspense SSR Architecture) and Qwik (Resumability). All statistics mentioned were verified at the time of publication.