All topics
Performanceadvanced

The @defer Block

Explain how @defer lazily loads and renders a section of a template based on triggers like viewport visibility or interaction.

@defer, introduced alongside the new control flow syntax in Angular 17, lets you lazy-load and lazy-render an entire section of a template — not just a route, but any arbitrary chunk of markup and the components it depends on — based on a configurable trigger, dramatically extending the granularity at which lazy loading can be applied compared to route-level loadComponent/loadChildren alone.

It's like a restaurant menu that only prints and hands you the dessert section once you've actually finished your main course (the trigger), rather than printing every possible course on one giant menu handed to you the moment you sit down.

Key Concepts

1
A @defer block's content (and the JavaScript for any components used inside it) is split into a separate chunk by the build tool and only fetched and rendered once its trigger condition fires — common triggers include on viewport (render once the block scrolls into view, ideal for below-the-fold content), on interaction (render after the user clicks or focuses a specified trigger element, ideal for content behind an initially-collapsed accordion or tab), on idle (render once the browser is idle, a good default for lower-priority content that should still load reasonably soon), and on timer(duration).
@deferon viewporton interactionon idleon timer(duration)
2
Each @defer block optionally pairs with @placeholder (content shown before the trigger fires), @loading (content shown while the deferred chunk is being fetched, useful for a spinner during a slow network), and @error (content shown if the deferred chunk fails to load) — giving you fine control over the loading experience without hand-rolling that state machine yourself the way you would have needed to with a manual NgComponentOutlet-based approach.
@defer@placeholder@loading@errorNgComponentOutlet
3
A strong interview answer frames @defer as meaningfully different from route-level lazy loading in scope: route-level lazy loading operates at the granularity of "this entire page," while @defer operates at the granularity of "this specific widget/section within a page," making it possible to keep a single page's initial bundle lean even when that page contains several heavy, below-the-fold, or rarely-interacted-with widgets (comment sections, complex charts, secondary content) that most visits won't ever need to fully load.
@defer