ReviseAlgo Logo

Advanced

Performance

Master Angular Performance Optimization, covering NgOptimizedImage, Deferrable Views (@defer), code splitting, and list tracking.

Last Updated: July 15, 2026 12 min read

1. Learning Objectives

In this lesson, you will master Angular performance optimization techniques. By the end of this topic, you will be able to:

  • Optimize image delivery and prevent Cumulative Layout Shift (CLS) using NgOptimizedImage.
  • Implement deferrable views using the modern @defer block template syntax.
  • Select and configure appropriate loading triggers (idle, viewport, hover, interaction) for lazy code chunk loading.
  • Structure route-level lazy loading to optimize initial application bundle size.
  • Optimize list rendering in templates using the track expression inside loops.

2. Overview

Angular Performance Optimization involves techniques that reduce the size of initial load bundles and speed up component rendering loops. By splitting large modules into deferred chunks that only load on-demand, caching rendering outputs, and optimizing asset delivery (like images), you can achieve high core web vitals. Key APIs include NgOptimizedImage, the @defer template engine, and route-level lazy loading.

3. Why This Topic Matters

Poor optimization results in high bounce rates and poor search engine rankings:

  • Initial Bundle Bloat: If you import a heavy charting library or comments widget on your home page, users must download megabytes of JavaScript before they see anything, severely impacting Largest Contentful Paint (LCP).
  • Layout Shifts: Images loading without explicit dimensions cause content to jump around the page during loads. This poor user experience, called Cumulative Layout Shift (CLS), is heavily penalized by search engines.

4. Real-World Analogy

Think of optimizing your Angular application like **planning a moving truck**:

  • Unoptimized loading: Packing every item you own (winter clothes, lawn mowers, old books) into the first truck. You spend hours moving items you won't need for months, making the move slow and tiring.
  • Deferred views (@defer): Packing only what you need for the first day (bed sheets, toothbrush, work laptop). You leave the lawn mowers and winter clothes in storage (split chunks) and only call the delivery service (triggers) to bring them when it's spring (on viewport) or when you need them (on interaction).

5. Core Concepts

Below are the primary mechanisms for loading template content lazily:

Trigger Option Description Ideal Use Case
on idle Loads chunk in the background after browser becomes idle (Default trigger). Below-the-fold content that is likely to be viewed soon.
on viewport Loads chunk when placeholder enters browser viewport (Intersection Observer). Heavy charts, maps, or image sliders further down the page.
on interaction Loads chunk when user clicks or focuses on the placeholder element. Expansions, dropdowns, modal windows, or dynamic tabs.
on hover Loads chunk when user hovers mouse over the placeholder. Detailed tooltips, previews, or help panels.

6. Syntax & API Reference

This template shows how to implement deferrable views with placeholders, loading templates, and error boundaries:

7. Visual Diagram

This diagram displays the state transitions of a Defer block lifecycle:

8. Live Example — Full Working Code

Below is a complete optimized dashboard component that lazy loads an interactive comment panel when hovered over, and manages image optimizations:

What just happened? Under normal circumstances, CommentSectionComponent's code compiles directly into the main dashboard bundle. By placing it inside @defer (on hover), Angular splits it into a separate JavaScript chunk. The user initially sees the dashed placeholder box. The moment the mouse hovers over this placeholder, the browser fetches the comment section bundle and renders it seamlessly.

9. Interactive Playground

Try It Yourself Challenges:

  1. Change the dashboard trigger from on hover to on interaction. Verify in your browser DevTools network tab that clicking the placeholder launches the module download.
  2. Test combining triggers: write @defer (on viewport; when dataLoaded) where dataLoaded is a boolean component variable. Verify the module only loads when both criteria are met.

10. Common Mistakes

Mistake Why it happens Wrong Correct
Omitting Width/Height on NgOptimizedImage Failing to set dimensions (unless using fill layout) throws compiler warnings and prevents layout shift calculations. <img ngSrc="img.png"> <img ngSrc="img.png" width="300" height="200">
Forgetting Track in loops Using loops without tracking ID causes Angular to delete and re-recreate every single DOM node on array changes. @for (item of items; track $index) (if list order changes) @for (item of items; track item.id)

11. Best Practices

  • Use track inside loops: Always provide a unique tracking key (e.g. track item.id) in loops rather than falling back to array index tracking.
  • Set image priority: Enable the priority attribute on images located in the initial viewport (above-the-fold) to trigger preload optimizations.
  • Add placeholder boundaries: Provide a height to placeholder blocks inside @defer templates to prevent page layout jumps when components load.
  • Lazy load route components: Leverage loadComponent in routing configurations to split pages into distinct JS bundles.

12. Browser Compatibility/Requirements

Deferrable views and image directives run in all modern web browsers. Viewport tracking leverages the native browser `IntersectionObserver` API.

13. Interview Questions

Q1: What are the main benefits of the NgOptimizedImage directive?

Answer:

  • Prevents layout shifts (CLS) by enforcing dimension ratios.
  • Generates image source-sets (srcset) automatically for differing screens.
  • Auto-enables lazy loading for below-the-fold images.
  • Warns developer if images are scaled poorly relative to parent dimensions.

Q2: How does the @defer block differ from using *ngIf for hiding components?

Answer: *ngIf only hides or shows components logically in the template, but the component's JavaScript code must still be downloaded, parsed, and executed in the main initial bundle. The @defer block completely splits the component into a separate bundle chunk that is only downloaded from the server when specified triggers fire.

14. Debugging Exercise

Identify why deferrable view split chunk is loaded immediately on page load, rendering the placeholder useless:

View Solution

Diagnosis: Defer blocks trigger chunk splitting for components declared *inside* the block. However, if the component is imported or referenced elsewhere outside a defer block, or if the template placeholder is positioned inside the active initial view (meaning the viewport trigger condition matches immediately on startup), the chunk loads eagerly.

Fix: Ensure the placeholder element is set below the initial viewport scroll boundary, and confirm the deferred child component isn't explicitly imported or rendered elsewhere eagerly in the layout.

15. Practice Exercises

Exercise 1: Defer map rendering below viewport

Build a layout containing a heavy Google Map stub component. Wrap the map in a @defer block using the on viewport trigger, ensuring the map is placed far enough down to require scrolling to initiate code loading.

16. Scenario-Based Challenge

The Dynamic News Feed Challenge:

You are designing a high-traffic news platform. The homepage lists 100 articles, each displaying author images and widgets. Rendering all nodes concurrently slows scrolling down. Design an optimization plan combining @defer blocks for comment frames, NgOptimizedImage priorities for banners, and custom loops with unique id tracking keys to ensure infinite scrolling runs at 60 FPS.

17. Quick Quiz

Q1: Which directive attribute should you apply to prioritize preloading for main banner images?

A) loading="lazy"

B) priority

C) preload="true"

Answer: B — Applying the 'priority' attribute triggers image preloading flags in the browser.

18. Summary & Key Takeaways

  • Image optimizations (NgOptimizedImage) prevent layout shifts (CLS).
  • Deferrable views (@defer) split template components into separate JS chunks.
  • Triggers like viewport and interaction control when code is loaded.
  • Loop optimizations require unique tracking expressions to prevent DOM rebuilds.

19. Cheat Sheet

Directive / Blocks Purpose
<img ngSrc="url"> Enforces optimized image loading patterns.
@defer (on viewport) {} Splits contents and loads chunk when scrolled into view.
@placeholder (minimum Xms) Displays fallback content prior to trigger execution.
---