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:
- Server renders the block contents and emits hydration annotations.
- Client boots without hydrating the deferred subtree.
- Trigger fires on the client.
- Lazy chunks load.
- Runtime walks the deferred shell using the hydration annotations.
- 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@deferbecause 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
- A new trigger: add the parser support in
packages/compiler/src/render3/view/template.ts, the runtime trigger inpackages/core/src/defer/triggering.ts, and the matching codegen. - A new block parameter (e.g., a new tunable for
@loading): extend the AST node, the IR op, the runtime state machine, and the type-check generator. - Hydration of a new construct: extend
packages/core/src/defer/incremental_hydration.tsand coordinate withplatform-server.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.