Lazy Hydration in Nuxt

This guide is part of Vue & Nuxt Performance, within Framework Performance. A server-rendered Nuxt page arrives as complete HTML, but it is not interactive until hydration has run: Vue loads the components' JavaScript, runs their setup, rebuilds state and attaches event listeners. By default, every component on the page hydrates immediately after load — the header, the hero, the product description, the reviews section, the recommendations carousel and the footer — in one burst of main-thread work.

Most of that work is unnecessary at load time. Components below the fold will not be used for seconds, and some (static text, footers) never need interactivity. Lazy hydration delays hydrating a component until a trigger: when it becomes visible, when the browser is idle, when the user interacts with it, after a delay, or when a media query matches. Server components go further, rendering on the server with no client JavaScript at all. Together they shrink the hydration burst, reducing TBT and the INP of early interactions.

Hydration work after load: eager vs lazy Timeline comparing main-thread hydration work for eager hydration of all components and lazy hydration of below-the-fold components. Hydration work after load: eager vs lazy Eager hydrate all components Lazy hero reviews 0ms 250ms 500ms 750ms 1000ms 1250ms 1500ms 1750ms 2000ms interactive (lazy) interactive (eager)

Rapid Diagnosis

  • Record a load trace with CPU throttling; find the hydration long task after the app bundle runs.
  • List components on the page and mark which are interactive and above the fold.
  • Check for static content components (footers, SEO text, legal blocks) that hydrate.
  • Check bundle chunks: below-the-fold components' code should be split out to benefit from lazy hydration.

Root Cause Analysis

1. Eager hydration of everything. All components hydrate at once.

2. Static content as client components. Components without interactivity still hydrate.

3. Heavy below-the-fold widgets. Carousels, maps and reviews hydrate during load.

4. Shared chunks. Lazy components imported statically are still in the main bundle.

Step-by-Step Resolution

1. Use hydration triggers on lazy components

html
<template>
  <ProductHero :product="product" />
  <LazyProductReviews hydrate-on-visible :id="product.id" />
  <LazyRecommendations hydrate-on-idle />
  <LazyNewsletterSignup hydrate-on-interaction="focus" />
  <LazySiteFooter hydrate-never />
</template>
<!-- trade-off: before hydration, components are static HTML; clicks on a not-yet-
     hydrated button do nothing unless the trigger is interaction-based. -->

Prefixing a component with Lazy splits its code into a separate chunk; the hydrate-* props control when it hydrates.

2. Make static sections server components

Rename static components to .server.vue (with server components enabled) so they render only on the server and ship no client code.

3. Choose triggers by component role

Visible for below-the-fold sections, interaction for forms and widgets used occasionally, idle for content that should become interactive soon without competing with load, never for static content.

4. Reserve space

Lazy-hydrated components are server-rendered, so their layout is present; client-only components (<ClientOnly>) need sized placeholders to avoid CLS.

Choosing a hydration trigger Recommended lazy hydration triggers for common component roles. Choosing a hydration trigger Component Trigger Reason Reviews, recommendations hydrate-on-visible used after scrolling Newsletter form hydrate-on-interaction rarely used Chat launcher hydrate-on-idle available soon after load Footer, legal text hydrate-never or .server.vue no interactivity Above-the-fold controls eager needed immediately

Verification

Record a load trace again: the hydration long task should be shorter, with later small tasks as components hydrate on their triggers. Scroll and interact to confirm components hydrate and work. In the field, compare TBT-related metrics and INP for early interactions.

Worked Example: A Travel Guide Site

A travel guide site's destination pages rendered a hero, an article with an interactive table of contents, a photo gallery, a map, a weather widget, reviews, related destinations and a large footer. Hydration took 900ms on a mid-range phone; INP for tapping the table of contents during the first seconds was over 400ms. The team made the article body and footer server components, lazy-hydrated the gallery and map on visibility, the weather widget on idle, and reviews on visibility. The initial hydration task dropped to 220ms, TBT by 650ms, and INP p75 from 290ms to 160ms.

How Lazy Hydration Interacts With Client Navigation

On client-side navigation, components render on the client rather than hydrating server HTML. Lazy components still load their code on demand, but hydration triggers apply mainly to the initial server-rendered page. Make sure lazy components render correctly in both modes, and test navigations as well as first loads. Components marked hydrate-never still render on client navigation (they have no server HTML to keep), so they should be lightweight or replaced with server components.

Measuring the Effect

Hydration improvements show most clearly in lab traces (shorter initial long task, lower TBT) and in field INP for interactions during the first seconds. Use Long Animation Frames attribution to confirm that hydration frames shrank. Track the number of components hydrated on load as a simple metric in lab tests; it is a good leading indicator before field data catches up.

Initial hydration task on destination pages (mobile profile) Bar chart of the initial hydration long task duration before and after server components and lazy hydration. Initial hydration task on destination pages (mobile profile) Eager hydration 900ms + server components for static parts 480ms + lazy hydration triggers 220ms target

Rolling Out Lazy Hydration Safely

Start with components that are clearly below the fold on every viewport and have no early interactions: reviews, related content, footers. Apply hydrate-on-visible or convert them to server components, then test scrolling and interacting on a throttled device. Next, look at widgets that users rarely touch (newsletter forms, share menus) and use interaction triggers. Leave above-the-fold controls eager. After each step, compare the hydration trace and check for regressions such as buttons that appear but do not respond, or content that changes when it hydrates (a sign of hydration mismatches).

Common Mistakes

  • Lazy-hydrating above-the-fold controls. Users click and nothing happens.
  • Static imports of lazy components. Code stays in the main bundle.
  • hydrate-never on interactive components. They never work.
  • No placeholders for client-only components. Layout shifts.

Edge Cases

Nuxt version. Built-in hydration triggers are available in recent Nuxt versions; older versions need community modules.

Third-party components. Wrap them in lazy components to defer both code and hydration.

State shared across lazy components. State initialised during SSR is in the payload; lazy components read it when they hydrate.

Accessibility. Ensure non-hydrated content is accessible as static HTML (links work, content readable).

FAQ

What is lazy hydration?

Delaying the hydration of a server-rendered component until a trigger, such as visibility, idle time or interaction, instead of hydrating it immediately on load.

Does lazy hydration hurt SEO?

No. The HTML is still server-rendered; only the JavaScript activation is delayed.

What happens if a user clicks before hydration?

With visibility or idle triggers, the click may do nothing until hydration completes. Use interaction triggers for components users may click early.

What are Nuxt server components?

Components (.server.vue) rendered only on the server, sending HTML without client JavaScript. They cannot have client interactivity.

Is lazy hydration the same as lazy loading components?

Related but different. Lazy loading defers the component's code; lazy hydration also defers running it on server-rendered HTML.

Does lazy hydration improve LCP?

Usually little, since LCP depends on server HTML and resources. It mainly improves TBT and INP.

Can I lazy-hydrate based on screen size?

Yes, with a media query trigger, for example hydrating a desktop-only sidebar only on large screens.

How do I know which components to make lazy?

Start with components below the fold and heavy widgets; check the hydration trace and bundle analysis for the largest contributors.

Does lazy hydration change the HTML?

No. Components are still server-rendered; only the client-side activation is deferred.

Can lazy hydration cause hydration mismatches?

Not by itself. Mismatches come from differences between server and client rendering, such as dates or random values; fix those regardless of hydration timing.