LearnDeferred rendering and hydration

Lazy Component Policy #

Defer offscreen browser work with attributes on a component's root <template>. Rendering and hydration are separate: rendering is layout and paint; hydration attaches bindings and listeners.

Hydration explains lifecycle behavior, while Performance explains when deferral is worthwhile.

PolicyRendering before activationHydration triggerRecommended use
No directiveNormalEager when the definition loadsVisible, first-use-critical UI
w-hydrate="lazy"NormalViewport relevanceOffscreen UI where rendering containment is unsafe
w-render="lazy" + reservationOffscreen layout/paint skippedViewport relevanceRepeated or numerous offscreen components
w-hydrate="interaction"NormalPointer, focus, keyboard, or click intentOne optional visible shell or island
w-render="lazy" + reservation + w-hydrate="interaction"Offscreen layout/paint skippedInteraction intentOne optional offscreen singleton

Full Work Reduction #

<template
  w-render="lazy"
  w-reserve-block-size="18rem"
>
  <article>...</article>
</template>

This is the recommended policy for component types repeated below the initial viewport. It combines:

  • content-visibility: auto for browser-managed style, layout, paint, and raster deferral
  • contain-intrinsic-block-size: auto 18rem to preserve scroll geometry and remember the measured size after rendering
  • visibility-deferred hydration through the optional @microsoft/webui-framework/lazy-hydration.js coordinator

w-reserve-block-size is required with w-render="lazy". Use a typical rendered block size for one instance. The compiler accepts one non-negative CSS length, including absolute, font-relative, viewport, and container-query units. It rejects percentages, negative lengths, keywords such as auto, and functions such as calc().

Hydration Only #

<template w-hydrate="lazy">
  <article>...</article>
</template>

This advanced policy leaves rendering unchanged and defers only JavaScript hydration. Use it when content-visibility containment is unsafe for the component's layout.

Do not pair w-render="lazy" with w-hydrate="lazy" because rendering deferral already includes visibility-deferred hydration.

Interaction Application #

<template w-hydrate="interaction">
  <nav>...</nav>
  <outlet />
</template>

This application-root policy keeps the trusted SSR DOM active while deferring the authored component graph and router until interaction. The compiler marks the rendered root. Compose @microsoft/webui-framework/interaction-hydration.js with the framework-agnostic @microsoft/webui-router/preload.js handle to prefetch one hovered route partial, then activate components and router without refetching.

Use this only once in the active document, on a component with a matching authored .ts or .js module. See Interaction-triggered hydration.

Lazy Rendering Until Interaction #

For one offscreen SSR island that may never be used, combine the rendering and interaction policies:

<template
  w-render="lazy"
  w-reserve-block-size="18rem"
  w-hydrate="interaction"
>
  <button @click="{open()}">Open</button>
</template>

The browser can skip offscreen style, layout, paint, and raster work while the component module graph remains unloaded. When interaction starts the graph, this boundary hydrates eagerly before click replay; it does not also wait for the visibility coordinator.

The interaction marker still has document-wide singleton semantics. Do not use this combination for repeated list items. For a visible application shell, content-visibility provides no initial rendering benefit: put w-hydrate="interaction" on the shell and w-render="lazy" on repeated offscreen descendants instead.

Per-Instance Overrides #

<!-- Keep rendering deferral, but hydrate immediately. -->
<product-card w-hydrate="eager"></product-card>

<!-- Disable rendering and hydration deferral. -->
<product-card w-render="eager"></product-card>

Only these exact, case-sensitive values are recognized.

Build and Runtime Behavior #

The policy attributes are build-only and do not select a DOM mode. An authored <template shadowrootmode="open"> remains Shadow; otherwise the component follows the build's --dom fallback (Shadow by default). In Light mode the policy wrapper is unwrapped and its content renders directly in the host.

Rendering deferral styles apply before component JavaScript loads, including for instances inside another component's Shadow root. Pass the response CSP nonce through your renderer when inline styles require one.

The SSR subtree remains in the DOM, so text and accessibility semantics are preserved. This policy does not defer HTML parsing, DOM node construction, custom-element definition, or resource discovery.

See Hydration for coordinator loading, browser fallback behavior, interaction activation, and image guidance.