Skip to main content
Angular Basics
CHAPTER 24 Beginner

Angular Performance Optimization

Updated: May 18, 2026
5 min read

# CHAPTER 24

Angular Performance Optimization

1. Chapter Introduction

A beautiful Angular app that runs slowly will lose users. Performance optimization in Angular involves reducing the amount of work the browser does — fewer DOM updates, smaller bundle sizes, deferred module loading. This chapter covers the essential techniques used by professional Angular teams.

2. Learning Objectives

  • Implement OnPush change detection strategy.
  • Use trackBy with *ngFor to avoid unnecessary DOM re-renders.
  • Implement lazy loading for modules and images.
  • Use the async pipe to prevent manual subscriptions.
  • Understand virtual scrolling for large lists.

3. Change Detection: Default vs OnPush

By default, Angular checks every component for changes on every single browser event (click, keyup, etc.). OnPush tells Angular to only re-render the component when its @Input references change or an Observable emits.
typescript
1234567891011
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'app-product-card',
  templateUrl: './product-card.component.html',
  // OnPush: Only update when @Input reference changes
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProductCardComponent {
  @Input() product: any;
}

Performance gain: In a list of 1,000 product cards, OnPush prevents 999 unnecessary checks when the user clicks a single button.

4. trackBy with *ngFor

Without trackBy, when the data array changes, Angular destroys and recreates ALL DOM elements. With trackBy, Angular only updates elements that actually changed.
typescript
1234567
export class ProductListComponent {
  products: any[] = [...]; // 1000 items

  trackByProductId(index: number, product: any): number {
    return product.id; // Angular uses the ID to identify items
  }
}
html
1234567
<!-- Without trackBy: ALL 1000 items re-render on any change -->
<li *ngFor="let product of products">

<!-- WITH trackBy: Only changed items re-render -->
<li *ngFor="let product of products; trackBy: trackByProductId">
  {{ product.name }}
</li>

5. Lazy Loading Modules

Already covered in Chapter 10, but worth emphasizing as the single biggest performance win for large apps:
typescript
12345
const routes: Routes = [
  { path: &#039;&#039;, component: HomeComponent },
  { path: &#039;admin&#039;, loadChildren: () => import(&#039;./admin/admin.module&#039;).then(m => m.AdminModule) },
  { path: &#039;reports&#039;, loadChildren: () => import(&#039;./reports/reports.module&#039;).then(m => m.ReportsModule) }
];

Result: Initial bundle might go from 5MB to 500KB, dramatically improving Time-to-Interactive.

6. Pure Pipes Over Methods in Templates

html
12345
<!-- BAD: Function called on every change detection cycle! -->
<li *ngFor="let product of getFilteredProducts()">

<!-- GOOD: Pure pipe only recalculates when input reference changes -->
<li *ngFor="let product of products | filterByCategory:selectedCategory">

7. Async Pipe Instead of Manual Subscriptions

typescript
12345
// BAD: Manual subscription — must remember to unsubscribe
export class Bad {
  users: User[] = [];
  ngOnInit() { this.userService.getUsers().subscribe(u => this.users = u); }
}
typescript
1234
// GOOD: async pipe — auto-manages subscription, enables OnPush compatibility
export class Good {
  users$ = this.userService.getUsers(); // Observable
}
html
1
<li *ngFor="let user of users$ | async">{{ user.name }}</li>

8. Virtual Scrolling for Large Lists

Only renders the items currently visible in the viewport:
bash
1
npm install @angular/cdk
typescript
1
import { ScrollingModule } from &#039;@angular/cdk/scrolling&#039;;
html
123
<cdk-virtual-scroll-viewport itemSize="50" style="height: 400px;">
  <div *cdkVirtualFor="let item of largeDataset">{{ item.name }}</div>
</cdk-virtual-scroll-viewport>

Rendering 10,000 items? Virtual scrolling renders only ~20 visible items at a time!

9. Bundle Optimization

bash
123456
# Build for production — enables tree-shaking, minification, AOT compilation
ng build --configuration=production

# Analyze bundle size
npm install -g source-map-explorer
source-map-explorer dist/my-app/main.js

10. Preloading Strategy

typescript
1234
import { PreloadAllModules } from &#039;@angular/router&#039;;

provideRouter(routes, withPreloading(PreloadAllModules))
// Lazy loads modules in the background after initial load

11. Common Mistakes

  • Method calls in templates: Methods in {{ getXyz() }} are called on every change detection cycle. Use pipes or async instead.
  • Not using OnPush on presentational components: "Dumb" components that only receive @Input should always use OnPush.

12. MCQs with Answers

Question 1

What is Angular's OnPush change detection strategy?

Question 2

What is the performance benefit of trackBy in *ngFor?

Question 3

What function does trackBy receive and what should it return?

Question 4

What is the single biggest performance optimization for large Angular apps?

Question 5

Why should you avoid method calls in templates like {{ getUsers() }}?

Question 6

What is the Angular CDK feature for efficiently rendering large lists?

Question 7

What Angular CLI command builds a production-optimized bundle?

Question 8

What optimization technique does the production build use to remove unused code?

Question 9

What preloading strategy loads lazy modules in the background after initial load?

Question 10

Which components benefit most from OnPush change detection?

13. Interview Questions

  • Q: Explain OnPush change detection. What kind of components should use it and why?
  • Q: What is virtual scrolling and why is it necessary for lists with thousands of items?

14. Summary

Performance optimization is not premature; it is professional engineering. By applying OnPush, trackBy, lazy loading, the async pipe, and virtual scrolling strategically, Angular applications remain fast and responsive at enterprise scale. These optimizations are expected knowledge in any senior Angular interview.

15. Next Chapter Recommendation

Quality applications require proof of correctness. In Chapter 25: Angular Testing, we learn how to write Unit Tests using Jasmine and Karma to verify that our components and services work exactly as intended.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·