Skip to main content

Deferred Hydration and Lazy Sections

A page can hold many sections, but a shopper sees only the first few. Deferred hydration lets the theme server-render and hydrate the top sections only. Every section below that point becomes a placeholder with a reserved height, and the engine upgrades it to real content when the shopper scrolls near it.

This reduces the initial HTML, and it reduces the JavaScript that the browser evaluates before the page becomes interactive.

A theme opts in. A theme that declares nothing keeps the previous behaviour.

The declaration

Declare deferredHydration on the theme bundle root, in theme/index.jsx.

export default {
// ... the rest of the theme bundle

deferredHydration: {
ssrSectionCount: 6, // Top body sections to server-render and hydrate
defaultMinHeight: 320, // Pixels reserved for a deferred section with no entry below
sectionMinHeights: { // Pixels reserved per section name
"featured-collection": 640,
"testimonials": 420,
},
rootMargin: "800px 0px", // Upgrade a section when it is this close to the viewport
},
};

ssrSectionCount

ssrSectionCount is the number of top body sections that the engine fully server-renders and hydrates. The engine counts body sections by position, from the top.

Set it high enough that everything a shopper sees without scrolling is server-rendered. A value of about 6 or more suits most home pages.

The value is a trade. A higher value renders more markup on the server and costs more hydration time. A lower value defers more sections and reserves more placeholders.

Precedence

The theme value wins when it is a number greater than zero. Otherwise the engine uses its own configured maximum. A theme that declares ssrSectionCount therefore needs no change from the operator.

The engine always renders header and footer sections. ssrSectionCount applies to body sections only.

Search engines receive every section

When the request comes from a bot, the engine server-renders all sections and ignores ssrSectionCount. Deferred hydration therefore does not remove content from a crawler, and it does not affect indexing.

Reserved heights

A deferred section renders as a placeholder before it upgrades. The placeholder must hold the height that the real content will occupy. If it does not, the page moves when the content arrives.

FieldMeaning
sectionMinHeightsPixels reserved for one named section
defaultMinHeightPixels reserved for a deferred section with no entry in the map

Give every deferred section that renders content its own sectionMinHeights entry, keyed by the section name. Calculate the value the same way as a skeleton height: H = ceil(cardCount / columns) x (cardHeight + rowGap).

Read Section Performance Rules, Rule 3, for the height calculation and the matched skeleton that pairs with this reserve.

warning

Do not give a reserve to a section that can render nothing. A section that returns null when a flag is off, when the shopper is logged out, or when a list is empty must have no sectionMinHeights entry. A reserve on such a section leaves permanent empty space.

The operator can also supply reserves through engine configuration. The engine merges the two, and the theme value wins for any section name that appears in both.

rootMargin

rootMargin is the margin that the engine gives its IntersectionObserver. A deferred section upgrades when it comes within this distance of the viewport.

The default is 800px 0px, which upgrades a section roughly one screen before the shopper reaches it. Raise it to upgrade earlier and to make a fast scroll safer. Lower it to defer more work.

Client-only sections

A section that cannot render on the server, or that only has content after a client fetch, should declare itself client-only. Add this line to the section source file:

export const ssr = false;

The CLI scans the section source for that exact line and writes ssr: false into the generated section registry. The engine then renders a placeholder for the section and upgrades it on the client.

Put the marker in the section source, not in the generated registry. The section-chunking generator rewrites the registry on every build, and it removes a value added by hand. Read Section Chunking for the generator.

A client-only section inside the server-rendered top region upgrades as soon as it mounts. A client-only section below the cutoff waits until the shopper scrolls near it, so its chunk is evaluated outside the hydration window.

Do not add a second gate

The ssr: false marker already defers the section. Do not also wrap the section in a mounted or isRunningOnClient() check that returns null on the first render. The two gates together collapse the placeholder to zero height and move the page. Render the skeleton on the first render instead.

Height pinning

After hydration the engine holds the server-rendered heights for a short time, then releases them. heightPinning tunes that release.

FieldDefault
idleBufferMs2000
maxWaitMs15000
noRicFallbackMs10000

Leave these at the defaults unless you have measured a reason to change them. The engine accepts a finite number between 0 and 120000. It ignores any other value, keeps the default, and writes a warning to the log.

Route prefetch

routePrefetch declares the routes whose chunks the engine fetches ahead of a likely navigation.

routePrefetch: {
routes: ["product-listing", "product-description", "cart-landing"],
},

Each entry is a route pageType identifier. Declare the three to five routes a shopper most often opens next, and keep the list short. Omitting routePrefetch disables prefetch, which is a valid choice rather than a defect.

Verification

  1. Open a long page and view the page source. Count the fully rendered body sections. The count must equal ssrSectionCount.
  2. Confirm that the header and the footer are present in the source.
  3. Scroll slowly. No deferred section may move the content below it when it upgrades.
  4. Scroll quickly to the bottom and back. Every section must have real content and correct heights.
  5. Request the page as a crawler. Every section must be present in the HTML.
  6. Confirm that a section which renders nothing leaves no empty gap.

Was this section helpful?