Deferring Components with Angular @defer Blocks

This guide is part of Angular Performance, within Framework Performance. Lazy-loading components in Angular used to require dynamic imports and manual view container management. Deferrable views make it declarative: wrap part of a template in @defer, and Angular splits the dependencies of that block into a separate chunk, loads it when a trigger fires, and renders it, showing @placeholder and @loading content in the meantime. Triggers include on viewport, on interaction, on hover, on idle, on timer, on immediate and custom conditions with when.

@defer is the most direct way to shrink the initial bundle of an Angular page without restructuring routes: charts, editors, maps, comment sections, recommendation carousels and modals can all move out of the main chunk. With SSR, @defer also powers incremental hydration, keeping server-rendered blocks dehydrated until their hydration trigger fires.

@defer triggers and when to use them Angular deferrable view triggers and the kinds of components each suits. @defer triggers and when to use them Trigger Loads when Good for on viewport placeholder enters view below-the-fold sections on interaction user clicks or focuses placeholder editors, chat, comments on hover pointer hovers placeholder menus and previews on idle browser idle after load soon-needed widgets on timer(Xms) after a delay low-priority content

Rapid Diagnosis

  • Check build stats for heavy components in the initial chunk.
  • List components on key pages and mark those not needed for the first view.
  • Check for heavy libraries (charting, rich text, maps) imported by those components.
  • Check CLS around areas where deferred content would appear.

Root Cause Analysis

1. Everything in the initial chunk. Components and their libraries load with the page.

2. Below-the-fold heavy components. Loaded eagerly but used later or never.

3. Interaction-only features. Editors and chat loaded for all users.

4. Missing placeholders. Deferred content pops in and shifts the layout.

Step-by-Step Resolution

1. Wrap heavy, non-critical components in @defer

html
@defer (on viewport; prefetch on idle) {
  <app-price-history-chart [productId]="id" />
} @placeholder (minimum 300ms) {
  <div class="chart-skeleton" style="height: 320px" aria-hidden="true"></div>
} @loading (after 150ms; minimum 300ms) {
  <div class="chart-skeleton is-loading" style="height: 320px"></div>
}
<!-- trade-off: prefetch on idle downloads the chunk early so rendering on viewport
     is instant, at the cost of bytes for users who never scroll that far. -->

2. Use interaction triggers for optional features

html
@defer (on interaction) {
  <app-review-editor [productId]="id" />
} @placeholder {
  <button type="button" class="btn">Write a review</button>
}

3. Keep dependencies inside the block

Components, directives and pipes used only inside the @defer block must be standalone and not referenced elsewhere in the file's eager code, or they stay in the main chunk.

4. Size placeholders

Give placeholders the same dimensions as the final content to avoid layout shifts.

Initial JavaScript for a product page (compressed) Bar chart of initial JavaScript on an Angular product page before and after deferring heavy components. Initial JavaScript for a product page (compressed) All components eager 310KB Chart deferred 235KB + review editor and chat deferred 168KB + recommendations deferred 141KB

Verification

Check the build output: deferred components should appear in separate lazy chunks. In the Network panel, those chunks should load only when triggers fire (or prefetch triggers). Check CLS when deferred content renders. In the field, compare LCP, INP and TBT-related metrics for the page.

Worked Example: An Electronics Retailer

An electronics retailer's Angular product page loaded a price history chart (with a charting library), a Q&A section, a review editor, a 3D product viewer and recommendations eagerly. The initial bundle was 310KB compressed, and TBT on mobile was 900ms. The team deferred the chart and Q&A on viewport, the review editor on interaction, the 3D viewer on interaction (with a static image placeholder), and recommendations on viewport with idle prefetch. The initial bundle fell to 141KB, TBT to 260ms, INP p75 from 250ms to 150ms, and LCP improved by 300ms. Sized placeholders kept CLS unchanged.

Prefetching and Perceived Speed

Deferring code risks a delay when the trigger fires: the chunk must download before rendering. The prefetch clause separates loading from rendering — for example, on viewport; prefetch on idle downloads the chunk during idle time and renders when the block becomes visible. For interaction triggers, prefetch on hover or prefetch on viewport can hide most of the delay. The @loading block's after and minimum parameters prevent flicker for fast loads.

Incremental Hydration With @defer

With SSR and incremental hydration enabled, @defer blocks are rendered on the server, so users see the content immediately, but their JavaScript and hydration are deferred until a hydration trigger (hydrate on viewport, hydrate on interaction, hydrate on idle, hydrate never). This combines the SEO and LCP benefits of server rendering with the bundle and TBT benefits of deferring. Event replay ensures interactions with not-yet-hydrated blocks are captured and replayed after hydration.

html
@defer (hydrate on viewport) {
  <app-related-products [productId]="id" />
}
<!-- Server-rendered HTML is visible immediately; its code loads and hydrates when
     scrolled into view. trade-off: interactions before hydration are replayed later. -->

Client @defer vs incremental hydration Comparison of client-side deferred rendering and server-rendered deferred hydration in Angular. Client @defer vs incremental hydration @defer (on viewport) • Placeholder in server HTML • Content rendered on the client later • Needs sized placeholder @defer (hydrate on viewport) • Real content in server HTML • Code loads and hydrates later • Good for SEO-relevant sections

Finding Candidates for @defer

Start from the build's stats: list the largest dependencies in the initial chunk and trace which components import them. Charting, rich-text editing, maps, PDF viewers, date pickers with large locale data, and 3D or video players are common heavy hitters. Then look at the page layout: anything below the fold on a typical mobile viewport, behind a tab or accordion, or behind a button is a candidate. Defer the heaviest first and re-check the initial chunk after each change, since shared dependencies may still be pulled in by another eager component.

Monitoring Deferred Loading in the Field

Deferred chunks shift work later, so measure what happens when they load. Track how often each deferred block's trigger fires (a simple custom event when the block renders), how long the chunk takes to load on real devices, and whether INP for interactions inside deferred blocks is affected. If a block's trigger fires for most users soon after load, consider prefetching it on idle or loading it eagerly; if it rarely fires, the deferral is saving bytes for almost everyone.

Common Mistakes

  • Deferring above-the-fold content. Delays what users see first.
  • Referencing deferred components elsewhere eagerly. They stay in the main chunk.
  • Unsized placeholders. Layout shifts.
  • No prefetch for likely interactions. Visible delay on click.

Edge Cases

Nested @defer blocks. Supported; avoid cascades where each level waits for the previous.

Testing. Angular testing utilities can render deferred blocks in specific states for unit tests.

SSR without incremental hydration. On the server, @defer renders placeholders; content renders on the client.

Non-standalone dependencies. Must be standalone to be deferred.

FAQ

What does @defer do?

It lazy-loads the dependencies of a template block and renders it when a trigger fires, showing placeholder and loading templates meanwhile.

Which trigger should I use?

on viewport for below-the-fold content, on interaction for optional features, on idle for widgets needed soon after load.

Does @defer work with SSR?

Yes. Without incremental hydration, placeholders render on the server; with it, the real content renders on the server and hydration is deferred.

How do I avoid layout shifts?

Give placeholders and loading templates the same dimensions as the final content.

Can I prefetch deferred code?

Yes, with the prefetch clause and its own triggers, such as prefetch on idle or prefetch on hover.

Is @defer better than lazy routes?

They complement each other: lazy routes split by page, @defer splits within a page.

What is the minimum parameter?

It keeps a placeholder or loading template visible for a minimum time to avoid flickering when content loads quickly.

Can deferred blocks contain several components?

Yes. All dependencies used only inside the block are bundled into its lazy chunk.

Do deferred blocks affect SEO?

Client-side deferred content is not in the server HTML. For SEO-relevant sections, use incremental hydration so the content is server-rendered.