Open-Source Wikis

/

Angular

/

Features

/

Deferrable views

angular/angular

Deferrable views

The @defer block: lazy-loading template fragments based on a trigger (idle, viewport, hover, interaction). Loading happens at the bundler level — the compiler emits a separate chunk for the deferred subtree's dependencies.

What it looks like

@defer (on idle; prefetch on hover) {
<heavy-chart [data]="chartData()" />
} @placeholder {
<p>Chart loading...</p>
} @loading (after 100ms; minimum 1s) {
<spinner />
} @error {
<p>Could not load chart.</p>
}

Triggers:

Trigger Fires when
on idle The browser is idle (requestIdleCallback).
on viewport The block enters the viewport (intersection observer).
on viewport(triggerEl) A specified element enters the viewport.
on interaction The user interacts (click, keydown) anywhere in the placeholder.
on hover The pointer hovers anywhere in the placeholder.
on immediate As soon as the placeholder mounts.
on timer(<duration>) After a duration.
when <expr> A signal-driven custom condition becomes truthy.

prefetch on … and hydrate on … extend the same triggers for prefetching the bundle and (in SSR) for incremental hydration.

Compiler pipeline

graph LR
  Source["@defer { ... } @placeholder { ... }"] --> Parser["template.ts<br/>DeferredBlock AST"]
  Parser --> Resolver["compiler-cli/ngtsc<br/>(extracts dependencies)"]
  Resolver --> Manifest["Defer dependency manifest<br/>(per-block)"]
  Manifest --> IR["Render3 IR (defer ops)"]
  IR --> Runtime["ɵɵdefer / ɵɵdeferOnIdle / ..."]
  Manifest --> Bundler["Bundler creates per-block chunk"]
  Bundler --> Lazy["Lazy chunk loaded on trigger"]

The compiler analyzes which directives, components, and pipes the deferred block uses, deduplicates them, and emits a manifest. The bundler (@angular/build) reads the manifest and produces a per-block chunk that imports only the deferred dependencies. The runtime calls ɵɵdeferEnableTimerScheduling, registers the trigger, and dynamically import()s the chunk when the trigger fires.

Runtime

Implemented in packages/core/src/defer/:

File What it covers
instructions.ts ɵɵdefer* instructions emitted by the compiler.
triggering.ts The trigger system (idle, viewport, hover, interaction, timer).
prefetching.ts Prefetch coordination for prefetch on ... triggers.
incremental_hydration.ts Hydration of deferred views on the client.
defer_block_state.ts Per-block state machine: placeholder → loading → complete / error.

Lifecycle:

stateDiagram-v2
  [*] --> Placeholder
  Placeholder --> Loading: trigger fires
  Loading --> Complete: chunk loaded + view created
  Loading --> Error: chunk failed to load
  Loading --> Timeout: loading > minimum
  Complete --> [*]
  Error --> [*]

The @loading (after Xms; minimum Yms) parameters tune when the loading state shows and how long it sticks.

SSR + incremental hydration

In SSR mode, deferred blocks render their content on the server (so the user sees the full page) but are not hydrated on initial client boot. Hydration runs only when the trigger fires on the client:

  1. Server renders the block contents and emits hydration annotations.
  2. Client boots without hydrating the deferred subtree.
  3. Trigger fires on the client.
  4. Lazy chunks load.
  5. Runtime walks the deferred shell using the hydration annotations.
  6. Queued events that fired during the unhydrated window are replayed.

See systems/hydration for the full hydration story.

Triggers and signals

@defer (when expr) re-evaluates expr reactively. If expr is signal-driven, the runtime registers the active LView as a consumer of those signals. When a signal flips the expression to truthy, the trigger fires.

Integration points

  • @angular/compiler-cli — emits the per-block dependency manifest.
  • @angular/build (CLI repo) — reads the manifest and produces per-block chunks.
  • @angular/router — works with @defer because each route can configure its own defer-block trigger behavior.
  • @angular/platform-server — emits the hydration annotations the client uses for incremental hydration.

Where to start when modifying

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Deferrable views – Angular wiki | Factory