Under The Hood
frontend angular Angular 17+

Angular @defer Deferrable Views

Last updated
Prerequisites:
Angular component and standalone imports
Basic awareness of route-level lazy loading
  • angular
  • deferrable-views
  • performance
  • lazy-loading
  • bundle-size

Read at your depth

The practical view

@defer lets you lazy-load any block of template plus its dependencies: @defer (on viewport) { <app-chart /> } @placeholder { <div>Loading chart…</div> } @loading (minimum 500ms) { <p>Fetching…</p> } @error { <p>Failed to load.</p> }. The triggers are on viewport, on idle, on interaction, on hover, on timer(5s), on immediate, or a custom when(condition). Each state block can also be combined. The key practical win: the component inside @defer, and everything it imports, is split into a separate chunk fetched only when the trigger fires — so the initial bundle and first-paint cost shrink without restructuring your module graph.

The same idea in other frameworks

react equivalent
const Chart = lazy(() => import('./Chart'));
<Suspense fallback={<div>Loading…</div>}><Chart /></Suspense>

Legacy vs modern

Always-eager widget vs @defer (on viewport) widget

The eager version ships the heavy chart library in the initial bundle; the deferred version splits it into a chunk loaded only when the chart scrolls into view.

before → after
Eager widget
<section>
  <app-heavy-chart [data]="series" /> <!-- ❌ OLD: eager — chart lib ships in initial bundle -->
</section>
@defer (on viewport)
<section>
  @defer (on viewport) { <!-- ✅ NEW: @defer (on viewport) splits chart into a lazy chunk -->
    <app-heavy-chart [data]="series" />
  } @placeholder {
    <div>Chart will appear here…</div>
  }
</section>

Interview gotchas

Context

Interviewers ask this to check whether you understand that deferrable views are a compiler feature, not just a template attribute.

The mechanical answer

The Angular compiler analyzes the @defer block, collects the standalone components and dependencies used inside it, and emits them into a separate render function wrapped in a dynamic import(). The block's template is compiled into a separate deferred view definition, and the runtime only instantiates it after the import resolves. Because it is compiler-driven, dependency hoisting is automatic — you get fine-grained chunking without manually orchestrating loaders, and unused imports stay out of the main chunk.

Trap

The common trap is claiming @defer only postpones rendering (runtime) and does nothing for bundle size. In fact the primary benefit is compile-time code-splitting; if you use @defer on a component that is also imported eagerly elsewhere, the component stays in the main bundle and the split never materializes. Interviewers love that catch.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.