Skip to content

Lazy Loading

Components marked with static lazy = true are loaded only when they enter the viewport. The SSR-rendered HTML is visible immediately. The JavaScript module is fetched in the background when the user scrolls near the component.

When to use

  • Below-the-fold components that don't need interactivity on initial load (charts, comment threads, image galleries).
  • Heavy components with large dependencies that would slow down the initial page load.
  • Components that most users never scroll to (footer widgets, "load more" sections).

When NOT to use

  • For above-the-fold components, since they need to be interactive immediately.
  • For critical UI like navigation, auth forms, or CTAs, since these must hydrate eagerly.
  • For tiny components with negligible JS cost, where the overhead of lazy loading isn't worth it.

Basic usage

Add static lazy = true to your component class:

import { WebComponent, html, css } from '@webjsdev/core';

class HeavyChart extends WebComponent {
  static lazy = true;  // ← module loaded on scroll, not on page load
  static styles = css`:host { display: block; min-height: 400px; }`;

  render() {
    return html`<canvas></canvas>`;
  }
}
HeavyChart.register('heavy-chart');

How it works

  1. During SSR, the component is rendered normally as full HTML with Declarative Shadow DOM. The user sees the content immediately.
  2. The SSR pipeline skips the <link rel="modulepreload"> for lazy components (no eager download).
  3. Instead, a small inline script registers the component tag with the lazy loader.
  4. An IntersectionObserver (with 200px root margin) watches for the element.
  5. When the element enters the viewport, the module is fetched via import().
  6. The custom element class registers itself, upgrading the element so event listeners bind and state initializes.

Selective hydration

For even more control, use static hydrate = 'visible'. This defers the component's connectedCallback activation (not just the module load) until the element is visible:

class LazyComments extends WebComponent {
  static hydrate = 'visible';  // ← activation deferred until visible
  // ...
}

The difference: lazy defers the module download. hydrate = 'visible' defers the component's activation even if the module is already loaded. Use both together for maximum deferral.

MutationObserver

The lazy loader automatically watches for dynamically added elements via MutationObserver. If a lazy component is added to the DOM after page load (e.g. via client-side navigation), it will still be observed and loaded when visible.

Fallback

In environments without IntersectionObserver (SSR-only, older browsers), all lazy components are loaded immediately as graceful degradation.

Next steps