Angular

Last Updated: 12/19/2025

Defer Loading

  • Deferrable views, also known as @defer blocks, reduce the initial bundle size of your application by deferring the loading of code that is not strictly necessary for the initial rendering of a page.
  • Results in a faster initial load and improvement in Core Web Vitals (CWV), primarily Largest Contentful Paint (LCP) and Time to First Byte (TTFB).
@defer {
  <large-component />
}
  • The code inside the @defer block is split into a separate JavaScript file and loaded only when necessary
  • Deferrable views support a variety of triggers, prefetching options, and sub-blocks for placeholder, loading, and error state management.
  • Deferred content loads and renders once the specified trigger occurs or when condition is met.
  • By default, a @defer block is triggered when the browser state becomes idle.

Which dependencies are deferred?

  • Components, directives, pipes, and any component CSS styles can be deferred when loading an application.
  • Dependencies within a @defer block need to meet two conditions
    • They must be standalone. Non-standalone dependencies cannot be deferred and are still eagerly loaded, even if they are inside of @defer blocks.
    • They cannot be referenced outside of @defer blocks within the same file. If they are referenced outside the @defer block or referenced within ViewChild queries, the dependencies will be eagerly loaded.
  • The transitive dependencies of the components, directives and pipes used in the @defer block do not strictly need to be standalone; transitive dependencies can still be declared in an NgModule and participate in deferred loading.
  • Angular's compiler produces a dynamic import statement for elements used in the @defer block. The main content of the block renders after all the imports resolve. Angular does not guarantee any particular order for these imports.

Transitive Dependencies: When you install a direct dependency, it might require other packages to function correctly. These required packages are the transitive dependencies. They are not directly listed in your package.json, but they are still installed into your node_modules directory as part of the dependency resolution process.

Placeholder Content

  • @placeholder is an optional block that declares what content to show before the @defer block is triggered
@defer {
  <large-component />
} @placeholder (minimum 500ms) {
  <p>Placeholder content</p>
}
  • You can use any content in the placeholder section including plain HTML, components, directives, and pipes.
  • Dependencies of the placeholder block are eagerly loaded.
  • Placeholder block accepts an optional parameter to specify the minimum amount of time (seconds or milliseconds) that this placeholder should be shown after the placeholder content initially renders.
  • You can use this parameter to prevent fast flickering of placeholder content when deferred dependencies are fetched quickly.

Loading Content

  • @loading block is an optional block that allows you to show content while deferred dependencies are loading. It replaces the @placeholder block once loading is triggered.
@defer {
  <large-component />
} @loading (after 100ms; minimum 1s) {
  <img alt="loading..." src="loading.gif" />
}
  • @loading block accepts two optional parameters to help prevent fast flickering
  • minimum: minimum amount of time the loading content should be shown
  • after: the amount of time to wait after loading begins before showing the loading template

Error Content

  • An optional block that displays if deferred loading fails
  • The dependencies of the @error block are eagerly loaded
@defer {
  <large-component />
} @error {
  <p>Failed to load large component.</p>
}

Triggers

  • There are two types of triggers: on and when.
  • Multiple event triggers can be defined by separating them with a semicolon, ; and will be evaluated as OR conditions.

on

  • on specifies a condition for when the @defer block is triggered.
  • idle: Triggers when the browser is idle.
  • viewport: Triggers when specified content enters the viewport
  • interaction: Triggers when the user interacts with specified element
  • hover: Triggers when the mouse hovers over specified area
  • immediate: Triggers immediately after non-deferred content has finished rendering
  • timer: Triggers after a specific duration
@defer (on viewport) {
  <large-cmp />
} @placeholder {
  <div>Large component placeholder</div>
}
<div #greeting>Hello!</div>
@defer (on viewport(greeting)) {
  <greetings-cmp />
}

when

  • loads the deferred content when the condition becomes truthy.
@defer (when condition) {
  <large-cmp />
} @placeholder {
  <div>Large component placeholder</div>
}

Prefetch

  • lets you load the JavaScript associated with the @defer block before the deferred content is shown
@defer (on interaction; prefetch on idle) {
  <large-cmp />
} @placeholder {
  <div>Large component placeholder</div>
}

Example

  • trigger on hover or when flag is set true.
  • prefetch the component on idle
<button (click)="loadDeferedComponent()">Load Defered Component</button>

@defer (on hover; when showComponent; prefetch on idle) {
	<app-fetch-data></app-fetch-data>
} @placeholder {
	<h2>Fetch data placeholder</h2>
} @loading (minimum 3s) {
	<h2>Loading fetch data</h2>
} @error {
	<h2>Failed to load</h2>
}
loadDeferedComponent() {
    this.showComponent = true;
}

Best Practices

  • Avoid cascading loads with nested @defer blocks
  • Avoid layout shifts: avoid immediate, timer, viewport, and custom when triggers that cause the content to load during the initial page render.
  • Keep accessibility in mind: wrap your @defer block in an element with a live region, to ensure deferred content changes are announced to screen readers.