Below is a complete, polished, and deeply detailed article on Angular’s @defer, written as if for a high-quality technical blog.
It covers everything: what @defer is, why it exists, triggers, placeholders, loading/error states, interaction with SSR/hydration, best practices, performance impact, and — importantly — how it differs from using @if.
If you want, I can also format it for Medium/Dev.to/LinkedIn afterwards.
A Complete Guide to Angular’s @defer: The New Era of Template-Level Lazy Loading
Angular’s modern template syntax introduced a powerful new primitive: deferrable views, powered by the @defer block.
This feature represents one of the biggest shifts in Angular’s performance model — giving developers precise control over when parts of the UI load, how they hydrate, and why the user should or shouldn’t wait for a component to be ready.
If you’ve ever struggled with lazy loading at the router level, or tried hiding components behind conditionals to improve performance, @defer is the feature you always wished Angular had.
This article will teach you everything:
- What
@deferis and why it exists - How Angular treats deferred blocks
- All deferral triggers
- How placeholders, loading, and error templates work
- How it affects SSR/hydration
- Best practices and anti-patterns
- Why
@deferIS NOT the same as@if— and why that matters - Real-world examples & patterns
Let’s start with the “why”.
Why @defer Exists (and Why @if Isn’t Enough)
Historically, Angular lazy-loaded only at the route level using the router’s loadChildren.
But component-level lazy loading?
You had to use:
- Dynamic imports inside components
- ViewContainerRefs
- NgComponentOutlet
- Third-party helper libraries
- Or… hide heavy UI behind an
*ngIf
Except hiding components does not reduce bundle size.
<!-- Does NOT lazy load -->
@if (shouldShowChart) {
<heavy-chart></heavy-chart>
}
Even if that branch never runs:
- The component code is still downloaded
- Angular still compiles it
- JS bundle remains large
- Initial render slows down
This is where @defer steps in.
What @defer Does
@defer enables declarative lazy loading at the template level.
That means:
- The component inside the block is excluded from the initial JS bundle
- It is only downloaded when needed
- Angular automatically turns the block into a separately-built chunk
- You gain fine-grained control over when that chunk downloads
This yields real performance wins:
- Faster initial page load
- Earlier interactivity
- Better Core Web Vitals
- “Progressively-enhanced UI” without extra code
Basic Example
@defer {
<heavy-chart></heavy-chart>
}
This alone:
- Splits
heavy-chartinto its own lazy chunk - Loads it as soon as possible after the initial page is interactive
Angular defaults to “idle-time loading” when no trigger is provided.
Triggers — When Should the Deferred Block Load?
@defer supports several “deferrable triggers”.
You add them in parentheses like:
@defer (on viewport) { ... }
Let’s cover them all.
1. on idle (default)
Loads when the browser is idle — great for non-critical UI.
@defer (on idle) {
<analytics-widget />
}
Used for:
- Analytics
- Charts
- Comment sections
- Ads
2. on viewport
Loads when the element enters the viewport.
@defer (on viewport) {
<promo-banner />
}
Used for:
- Below-the-fold elements
- Infinite scroll sections
- Hero animations
Equivalent to “load when the user scrolls here”.
3. on interaction
Loads when the user clicks/touches/keys anywhere inside the block’s placeholder area.
@defer (on interaction) {
<payment-form />
}
Examples:
- “Sign in” form
- Chat widget
- Expanded accordion content
4. on hover
Loads on hover event.
@defer (on hover) {
<product-preview />
}
Great for:
- Tooltips
- Quick previews
5. on timer(duration)
Delays loading by a fixed time.
@defer (on timer(2000)) {
<suggestions />
}
Could be used for:
- Prefetching
- Non-essential UI
6. when <condition>
Loads when a boolean condition becomes truthy.
@defer (when isReady()) {
<checkout-flow />
}
This one feels like @if, but behaves differently (we’ll compare later).
Loading States — @placeholder, @loading, @error
Deferrable views support optional companion blocks.
@placeholder (shown before loading begins)
@defer (on viewport) {
<product-gallery />
} @placeholder {
<div class="ph">Loading gallery…</div>
}
Displayed immediately until loading is triggered.
@loading (shown while chunk is downloading)
@loading {
<spinner />
}
This appears after trigger fires, before actual component is ready.
@error (if the lazy chunk fails to load)
@error {
<p>Failed to load content.</p>
}
Critical for robustness.
Full Example (Most Common Pattern)
@defer (on viewport) {
<heavy-chart></heavy-chart>
} @placeholder {
<p>Chart coming up…</p>
} @loading {
<p>Loading chart…</p>
} @error {
<p>Could not load chart.</p>
}
This is the production-ready form of @defer.
Why @defer Is NOT the Same as Using @if
This is where developers often get confused.
Let's clarify the difference clearly and absolutely.
❌ @if does not lazy load anything
@if (showChart) {
<heavy-chart />
}
- Component is still bundled in main chunk
- JS still downloaded
- Compilation still done
- Memory still allocated
@if only controls visibility, not loading.
✅ @defer actually lazy loads the code
@defer (on viewport) {
<heavy-chart />
}
- Component code is excluded from main bundle
- Chunk is created separately
- Angular only loads it when triggered
- Increases page load speed
- Improves TTI (Time to Interactive)
Defer vs If: Visual Comparison
| Feature | @if |
@defer |
|---|---|---|
| Lazy loads component? | ❌ No | ✅ Yes |
| Reduces JS bundle size? | ❌ No | ✅ Yes |
| Useful for performance? | ❌ Not really | ⭐ Essential |
| Controls visibility | ✅ Yes | ⚠️ Kinda (but different) |
| Controls when code loads | ❌ No | ✅ Yes |
| Needs triggers | ❌ No | ✅ Yes |
| SSR friendly | ✓ Yes | ✓ Yes |
| Hydration aware | ⚠️ Yes | ⭐ Optimized |
@defer is about performance.
@if is about logic.
Where to Use Each
Use @if for:
- Conditional UI logic
- Showing/hiding error messages
- Mode switching
- Stateful toggles
Use @defer for:
- Heavy components
- Large forms
- Third-party widgets
- Charts/maps
- Anything below-the-fold
- Expensive initialization
SSR + Hydration Behavior
When using Angular Universal:
With SSR:
- Server renders the placeholder (!)
- Browser loads actual heavy content only when triggered
- Prevents delaying First Contentful Paint (FCP)
Hydration becomes smarter:
-
@deferareas hydrate later - Event replay still works
- Large interactive areas do not block main thread early
This drastically improves:
- LCP
- FID
- TTI
- CLS stability
Best Practices
✔ Prefer @defer (on viewport) for below-fold content
✔ Combine @placeholder + @loading for smooth UX
✔ Don’t over-defer tiny components
✔ Use @defer (when condition) only when necessary
❌ Don’t rely on @if to optimize heavy components
✔ Defer third-party widgets by default
✔ Defer large forms on e-commerce/product pages
Anti-Patterns
🚫 Deferring above-the-fold core UI
Bad for UX; increases CLS.
🚫 Using @defer instead of routes for whole page sections
Use router lazy-loading for big chunks.
🚫 Using both @if and @defer to hide the same component
Pick one:
- Want lazy load? ⇒
@defer - Want visibility control? ⇒
@if
Practical Real-World Patterns
1. Defer Chat Widget
@defer (on interaction) {
<chat-support />
} @placeholder {
<button class="chat-btn">Chat with us</button>
}
User clicks → Chat loads instantly.
2. Defer Stripe or PayPal Payment Form
@defer (on interaction) {
<payment-checkout />
} @placeholder {
<button class="pay">Pay Now</button>
}
Avoid shipping heavy payment libraries on page load.
3. Defer Maps
@defer (on viewport) {
<company-map />
} @placeholder {
<div class="map-ph"></div>
}
Google Maps is huge — defer it always.
Final Summary
@if
- UI logic
- Does not improve performance
- Does not lazy load components
- Avoid using it for heavy UI parts
@defer
- Powerful UI performance optimization tool
- Lazy loads code at the template level
- Chunk splitting happens automatically
- Uses triggers:
on viewport,on idle,on interaction, etc. - Supports placeholders/loading/error views
- Helps with SSR, hydration, Core Web Vitals, UX
In modern Angular applications, @defer should be your go-to performance lever for delivering a fast, progressive user experience.
For the official Angular documentation on @defer, see:
👉 https://angular.dev/guide/templates/defer
Top comments (0)