Angular Performance Optimization Techniques Complete Guide

 Last Update:2025-06-22T00:00:00     .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    11 mins read      Difficulty-Level: beginner

Understanding the Core Concepts of Angular Performance Optimization Techniques

Angular Performance Optimization Techniques

1. Lazy Loading Modules

  • Explanation: Lazy loading involves deferring the loading of certain modules until they are needed. This technique splits your application into smaller, manageable chunks, reducing initial load time.
  • Implementation: Use Angular’s RouterModule to load modules asynchronously via route definitions.
const appRoutes: Routes = [
  {
    path: 'lazyModule',
    loadChildren: () => import('./lazy-module/lazy-module.module').then(m => m.LazyModuleModule)
  }
];

2. Ahead-of-Time (AOT) Compilation

  • Explanation: AOT compiles your Angular app into JavaScript code before deploying it. This results in faster load times for your users as there's no need to compile the application at runtime.
  • Implementation: Enable AOT when building your application by using the --aot flag.
ng build --aot

3. Tree Shaking

  • Explanation: Tree shaking is the process of removing dead code from your application. By only including the parts of your code that are used, you can significantly reduce your application’s size.
  • Implementation: Angular CLI’s ng build command is configured to perform tree shaking automatically when the --production flag is used.
ng build --production

4. Change Detection Optimization

  • Explanation: Angular’s Change Detection mechanism is one of the core processes that can significantly impact performance. Optimizing how Angular detects and updates changes can lead to better performance.
  • Implementation: Use OnPush strategy on components where the inputs are immutable or the changes are predictable. This reduces unnecessary checks.
@Component({
  selector: 'app-sample',
  templateUrl: './sample.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class SampleComponent {
  // component code
}

5. Avoiding Inline Templates and Styles

  • Explanation: Storing templates and styles in external files can help with debugging and maintenance. Furthermore, this separation aids in better caching and can improve load times.
  • Implementation: Always separate your component templates and styles into their own files.
@Component({
  selector: 'app-sample',
  templateUrl: './sample.component.html',
  styleUrls: ['./sample.component.css']
})
export class SampleComponent {
  // component code
}

6. Unsubscribe from Observables

  • Explanation: Memory leaks can occur if observables are not unsubscribed when they are no longer needed. This can lead to significant performance issues over time.
  • Implementation: Always unsubscribe from observables in the ngOnDestroy lifecycle hook.
import { Subscription } from 'rxjs';

export class SampleComponent implements OnDestroy {
  subscription: Subscription;
  
  constructor(private service: DataService) { 
    this.subscription = this.service.getData().subscribe(data => {
      // handle data
    });
  }
  
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

7. Using NgIf, NgFor, and NgSwitch Effectively

  • Explanation: Angular’s structural directives (*ngIf, *ngFor, *ngSwitch) can be used to efficiently conditionally render content, loop through lists, and toggle between views. Proper usage can prevent unnecessary DOM updates and improve performance.
  • Implementation: Use *ngIf to conditionally display content, *ngFor to loop over lists with minimal overhead, and *ngSwitch to switch between multiple views based on a condition.
<div *ngIf="isLoaded">
  <!-- content -->
</div>

<div *ngFor="let item of items">
  <!-- repetitive content based on items -->
</div>

<div [ngSwitch]="value">
  <div *ngSwitchCase="1">Content 1</div>
  <div *ngSwitchCase="2">Content 2</div>
  <div *ngSwitchDefault>Default Content</div>
</div>

8. Caching HTTP Responses

  • Explanation: Caching HTTP requests that return the same data repeatedly can reduce server load and improve response times.
  • Implementation: Use RxJS operators like shareReplay() to cache HTTP responses.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { shareReplay } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private data$: Observable<any>;
  
  constructor(private http: HttpClient) {}
  
  getData(): Observable<any> {
    if (!this.data$) {
      this.data$ = this.http.get('/api/data').pipe(
        shareReplay(1) // cache the result
      );
    }
    
    return this.data$;
  }
}

9. Reduce Third-Party Library Usage

  • Explanation: Every third-party library adds to the bundle size, which can slow down the initial load time. Carefully evaluate the necessity of each library and look for alternatives that are smaller or better performant.
  • Implementation: Replace heavy libraries with modular or smaller alternatives when possible.

10. Use Web Workers

  • Explanation: Web Workers allow background threads to run JavaScript code without blocking the main thread. This is particularly useful for computationally intensive tasks.
  • Implementation: Set up a Web Worker using the Angular CLI and perform heavy tasks in the background.
ng generate web-worker myWorker

By applying these techniques, you can enhance the performance of your Angular applications, ensuring a smoother and more responsive experience for your users. Always keep performance in mind during development and use profiling tools to identify and address performance bottlenecks.

Online Code run

🔔 Note: Select your programming language to check or run code at

💻 Run Code Compiler

Step-by-Step Guide: How to Implement Angular Performance Optimization Techniques

Complete Examples, Step by Step for Beginners: Angular Performance Optimization Techniques

Table of Contents:

  1. Change Detection Optimization
  2. Lazy Loading Modules
  3. TrackBy Function for ngFor
  4. OnPush Change Detection Strategy
  5. Reduce Bundle Size
  6. Avoid Memory Leaks
  7. Use Angular Universal for Server-Side Rendering

1. Change Detection Optimization

Change detection in Angular happens automatically, and Angular runs change detection checks on every event. This might lead to performance issues if not managed properly. Here's how we can optimize it:

Example: Optimize the change detection process

Suppose you have a parent component that contains a child component displaying the list of users. You only want to trigger change detection on the child component when the users actually change.

  • Parent Component HTML:
<!-- app.component.html -->
<div>
  <button (click)="addUser()">Add User</button>
  <app-user-list [users]="users"></app-user-list>
</div>
  • Parent Component Script:
// app.component.ts
import { Component } from '@angular/core';

interface User {
  id: number;
  name: string;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  users: User[] = [{ id: 1, name: 'John'}, { id: 2, name: 'Jane'}];
  userId: number = 3;

  addUser() {
    const newUser = { id: this.userId, name: `User ${this.userId}` };
    this.users = [...this.users, newUser]; // Creates a new array rather than mutating the existing one
    this.userId++;
  }
}
  • Child Component Script:
// user-list.component.ts
import { Component, Input, SimpleChanges } from '@angular/core';

interface User {
  id: number;
  name: string;
}

@Component({
  selector: 'app-user-list',
  template: `
    <ul>
      <li *ngFor="let user of users">{{ user.name }}</li>
    </ul>
  `
})
export class UserListComponent {
  @Input() users: User[] = [];

  ngOnChanges(changes: SimpleChanges) {
    console.log('Users changed:', changes.users);
    // Additional logic based on changes
  }
}

Explanation: Here, instead of mutating the existing array, we create a new one using the spread operator [...]. This ensures that Angular's change detection mechanism recognizes the change and updates the DOM accordingly.

2. Lazy Loading Modules

Lazy loading helps reduce the initial load time of your application by loading the necessary modules only when they're required.

Example: Lazy Load the Admin Module

Let's assume we have an Admin module in our Angular application and we want to lazy load it.

  • App Routing Module:
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  { path: 'home', loadChildren: () => import('./home/home.module').then(m => m.HomeModule) },
  { path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

Explanation: Instead of importing the AdminModule directly into the AppRoutingModule, we use a dynamic function loadChildren that imports the module lazily when the route is accessed.

3. TrackBy Function for ngFor

Using trackBy function with *ngFor directive can drastically improve the performance by avoiding unnecessary dom updates.

Example: Improve ngFor Performance with TrackBy Function

Suppose you display a list of products, and whenever the product list updates, you only want Angular to update the product items that have changed.

  • Component Script:
// product-list.component.ts
import { Component, Input } from '@angular/core';

export interface Product {
  id: number;
  name: string;
  price: number;
}

@Component({
  selector: 'product-list',
  template: `
    <ul>
      <li *ngFor="let product of products; trackBy: trackById">{{ product.name }} - ${{ product.price }}</li>
    </ul>
  `
})
export class ProductListComponent {
  @Input() products: Product[] = [];

  trackById(index: number, product: Product): number {
    return product.id;
  }
}

Explanation: The trackById function ensures Angular knows which DOM nodes correspond to which items in the array, thus reducing the number of DOM manipulations required during updates.

4. OnPush Change Detection Strategy

The OnPush strategy helps reduce the number of change detection cycles. It triggers a change detection only when a component's input properties change or an event originates within the component.

Example: Use OnPush Change Detector in a Component

Create a simple product details component:

  • Product Details Component Script:
// product-details.component.ts
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';

export interface Product {
  id: number;
  name: string;
  price: number;
}

@Component({
  selector: 'product-details',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <p><strong>ID:</strong> {{ product.id }}</p>
    <p><strong>Name:</strong> {{ product.name }}</p>
    <p><strong>Price:</strong> ${{ product.price }}</p>
  `
})
export class ProductDetailsComponent {
  @Input() product: Product | null = null;
}

Explanation: By setting the changeDetection property to ChangeDetectionStrategy.OnPush, the ProductDetailsComponent will only detect changes if its input properties (product) reference changes. This means that Angular won’t run change detection inside components unless an event occurs or new references are passed as inputs.

5. Reduce Bundle Size

Reducing the bundle size improves the load time of Angular applications.

Example: Reduce Bundle Size with Tree Shaking

Ensure you use AOT (Ahead-of-Time) compilation to enable tree shaking via the ng build --prod.

  • Update package.json to include build scripts:
"scripts": {
  "ng": "ng",
  "start": "ng serve",
  "build": "ng build --prod"
}

Explanation: When you run ng build --prod, Angular performs tree shaking, removing unused code from your bundle. This results in smaller bundle sizes and faster load times.

6. Avoid Memory Leaks

Memory leaks can significantly degrade application performance. Ensuring proper cleanup of subscriptions is crucial.

Example: Clear Subscriptions to Avoid Memory Leaks

Suppose you have a subscription to an observable stream in your component:

  • Component Script:
// user-profile.component.ts
import { Component, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { UserProfileService } from './user-profile.service';

@Component({
  selector: 'app-user-profile',
  template: `<p>User Name: {{ userName }}</p>`
})
export class UserProfileComponent implements OnDestroy {
  userName: string | undefined;
  private userProfileSubscription!: Subscription;

  constructor(private userProfileService: UserProfileService) {
    this.userProfileSubscription = this.userProfileService.getUserName().subscribe(name => this.userName = name);
  }

  ngOnDestroy() {
    if (this.userProfileSubscription) {
      this.userProfileSubscription.unsubscribe();
    }
  }
}

Explanation: The UserProfileComponent subscribes to an observable getUserName() from UserProfileService. To avoid memory leaks, we unsubscribe from the observable when the component is destroyed by implementing the OnDestroy lifecycle hook.

7. Use Angular Universal for Server-Side Rendering

Server-Side Rendering (SSR) boosts first contentful paint (FCP) and Time-to Interactive (TTI), making pages load faster and be more SEO-friendly.

Example: Setup Angular Universal

To set up Angular Universal for SSR, follow these steps:

Step 1: Install Required Packages

Run the following command to add Angular Universal packages:

ng add @nguniversal/express-engine

Step 2: Update App Server Module to Provide TransferState

  • app.server.module.ts:
// app.server.module.ts
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { TransferHttpCacheModule } from '@nguniversal/common';

@NgModule({
  imports: [
    AppModule,
    ServerModule,
    TransferHttpCacheModule
  ],
  bootstrap: [AppComponent],
})
export class AppServerModule {}

Explanation: TransferHttpCacheModule caches HTTP responses on the server and uses these cached responses for the client-side requests. This optimizes the loading time.

Step 3: Build the Universal App

Build your application for both client and server using:

ng run <app-name>:server
ng build --output-path dist/<app-name>/browser --prod

Step 4: Setup Express Server

  • server.ts:
// server.ts
import 'zone.js/node';

import { ngExpressEngine } from '@nguniversal/express-engine';
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
import { APP_BASE_HREF } from '@angular/common';

import express from 'express';

import { join } from 'path';
import { enableProdMode } from '@angular/core';
import { environment } from './src/environments/environment';
import { AppServerModuleNgFactory, LAZY_MODULE_MAP } from './dist/<app-name>/server/main';

enableProdMode();

const app = express();

const serverDistFolder = join(process.cwd(), 'dist/<app-name>/server');
const browserDistFolder = join(process.cwd(), 'dist/<app-name>/browser');

app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP)
  ]
}));

app.set('view engine', 'html');
app.set('views', browserDistFolder);

app.get('*.*', express.static(browserDistFolder, {
  maxAge: '1y'
}));

app.get('*', (req, res) => {
  res.render('index', { req });
});

const port = environment.port || 4000;
app.listen(port, () => {
  console.log(`Node Express server listening on http://localhost:${port}`);
});

Explanation: Here we configure an Express server to handle incoming requests and render Angular components on the server. The ngExpressEngine is used to integrate the Angular app with the Express server.


Top 10 Interview Questions & Answers on Angular Performance Optimization Techniques

1. What is Change Detection in Angular and how can you optimize it?

Answer: Change Detection is the process by which Angular keeps track of changes to the data model and updates the View accordingly. To optimize it, you can:

  • Use OnPush change detection strategy to reduce unnecessary checks on component inputs.
  • Employ ChangeDetectorRef to manually trigger or detach change detection in specific components for finer control.

2. How can you optimize loading times in Angular applications?

Answer:

  • Utilize Lazy Loading to defer loading of modules until they are needed.
  • Implement Ahead-of-Time (AOT) Compilation to produce highly optimized, ahead-of-time compiled JavaScript code.
  • Optimize your assets like images and fonts by compressing them.
  • Use HTTP compression and leverage browser caching.

3. How does Angular handle large lists and can this be optimized?

Answer: Handling large lists efficiently is crucial. You can use:

  • Virtual Scrolling: Display only a subset of items that are visible to the user, and swap out old ones as new ones come into view.
  • TrackBy Function: Helps Angular's change detection to efficiently track and update items in lists, particularly with dynamic data.

4. What are the best practices to minimize Angular application bundle size?

Answer: Reducing bundle size is key to faster load times. Use:

  • Tree Shaking: Angular CLI automatically removes unused code. Ensure your application imports are direct and only bring in necessary functionality.
  • Minification and Uglification: Enable these in the build process to reduce the size of your JavaScript files.
  • Lazy Load Feature Modules: Load modules only as needed.

5. How can you improve Angular's rendering performance?

Answer: Rendering performance can be enhanced by:

  • Using NgZone Efficiently: NgZone is responsible for running Angular's change detection. Avoid excessive operations within NgZone by marking native event handlers as non-angular or running them outside NgZone.
  • Optimizing Templates: Keep your templates simple and avoid heavy logic. Inline styles and templates where possible.

6. What are some strategies for efficient state management in Angular?

Answer:

  • RxJS: Use RxJS for state management to manage data streams efficiently.
  • NgRx: Implement NgRx for a scalable and predictable state management solution using 'redux' principles.
  • Akita: Consider Akita for a state management library that emphasizes performance and simplicity, also using RxJS under the hood.

7. How can you effectively debug performance issues in Angular?

Answer: Effective debugging starts with:

  • Performance Audits: Use tools like Lighthouse to audit and understand performance bottlenecks.
  • Profiler Tools in Browser DevTools: Analyze CPU usage, memory usage, and rendering performance to pinpoint issues.
  • Logging Change Detection: Use Angular DevTools to find out which components are checked during change detection.

8. How does the use of third-party libraries affect Angular performance and how can it be mitigated?

Answer: Third-party libraries can increase bundle size and affect performance:

  • Tree Shake Libraries: Ensure the library supports tree shaking to discard unused code during the build process.
  • Select Lightweight Libraries: Prefer lighter alternatives to heavy libraries.
  • Lazy Load Libraries: Load libraries on demand.

9. Can you provide tips for optimizing images and assets in Angular?

Answer:

  • Image Compression: Use image compression tools for reducing file sizes.
  • Responsive Images: Serve different resolutions of images based on the user's screen size.
  • WebP Format: Use the WebP format for better compression without significant quality loss.
  • Lazy Load Images: Load images only when they enter the viewport.

10. What impact does Server-Side Rendering (SSR) have on Angular performance?

Answer: Server-Side Rendering can significantly enhance application performance by:

  • Reducing Initial Loading Time: As the HTML is served from the server, users receive content faster, leading to better performance metrics.
  • Improving SEO: Search engines can crawl Angular applications better when content is pre-rendered.
  • Enhancing User Experience: Users see content faster, improving overall satisfaction.

You May Like This Related .NET Topic

Login to post a comment.