Common Practices That Kill Performance in Angular Applications

Erick Zanetti
3 min readJul 25, 2024

--

Developing high-performance Angular apps requires avoiding common pitfalls. Here are key practices that harm performance and how to fix them.

Angular bad performance

Developing high-performance applications with Angular requires attention to details that are often overlooked. Here are some common practices that can kill the performance of your Angular application, along with examples of bad code and their corrections.

1. Excessive Change Detection

Bad Code:

Change detection is triggered too frequently, affecting performance.

@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
})
export class MyComponent {
@Input() data: any;
}

Correction:

Use the OnPush strategy in components where possible. This makes change detection occur only when the component's input changes.

@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyComponent {
@Input() data: any;
}

2. Inefficient DOM Manipulation

Bad Code:

Direct DOM manipulations can be inefficient and hard to manage.

ngAfterViewInit() {
document.getElementById('elementId').style.color = 'blue';
}

Correction:

Use Angular’s Renderer2 to manipulate the DOM more efficiently.

import { Renderer2 } from '@angular/core';

constructor(private renderer: Renderer2) {}

ngAfterViewInit() {
const element = this.renderer.selectRootElement('#elementId');
this.renderer.setStyle(element, 'color', 'blue');
}

3. Overuse of Pipes

Bad Code:

Pipes that perform heavy operations can be re-executed frequently, hurting performance.

@Pipe({
name: 'heavyPipe',
pure: false
})
export class HeavyPipe implements PipeTransform {
transform(value: any): any {
// Heavy transformation logic
}
}

Correction:

Use pure pipes (pure: true) for lightweight operations or avoid pipes for heavy operations.

@Pipe({
name: 'heavyPipe',
pure: true
})
export class HeavyPipe implements PipeTransform {
transform(value: any): any {
// Light transformation logic
}
}

4. Excessive Initial Load

Bad Code:

Loading all modules and components at initialization can delay loading time.

@NgModule({
declarations: [AppComponent, FeatureComponent],
imports: [BrowserModule, FeatureModule],
bootstrap: [AppComponent]
})
export class AppModule {}

Correction:

Use lazy loading for modules that are not immediately needed.

const routes: Routes = [
{ path: 'feature', loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule) }
];

5. Unmanaged Subscriptions

Bad Code:

Not cancelling subscriptions can lead to memory leaks.

ngOnInit() {
this.myService.myObservable.subscribe(value => {
// Do something with value
});
}

Correction:

Use the async pipe or manage subscriptions with takeUntil or Subscription.

import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
styleUrls: ['./my-component.component.css']
})
export class MyComponent implements OnDestroy {
private destroy$ = new Subject<void>();

constructor(private myService: MyService) {
this.myService.getData()
.pipe(takeUntil(this.destroy$))
.subscribe(data => {
// Handle the data
});
}

ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}

For Angular 16+ use takeUntilDestroyed.

import { Component, OnDestroy } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
styleUrls: ['./my-component.component.css']
})
export class MyComponent implements OnInit, OnDestroy {

constructor(private myService: MyService) {
}

ngOnInit(): void {
this.myService.getData()
.pipe(takeUntilDestroyed(this))
.subscribe(data => {
// Handle the data
});
}

ngOnDestroy() {
}
}

6. Excessive Dependencies in Providers

Bad Code:

Registering too many providers in the root module increases the bundle size.

@NgModule({
providers: [MyService]
})
export class AppModule {}

Correction:

Register providers only where they are necessary.

@NgModule({
providers: [MyService] // Register only where necessary
})
export class FeatureModule {}

7. Lack of AOT Compilation

Bad Code:

Using JIT (Just-in-Time) compilation can be less efficient.

// No specific configuration for AOT

Correction:

Configure the application to use AOT (Ahead-of-Time) compilation.

{
"projects": {
"my-app": {
"architect": {
"build": {
"options": {
"aot": true
}
}
}
}
}
}

8. Lack of Bundle Optimization

Bad Code:

Unoptimized bundles increase loading time.

// Default Angular CLI configuration

Correction:

Use tools like Webpack and Angular CLI build configurations to optimize bundles.

{
"projects": {
"my-app": {
"architect": {
"build": {
"configurations": {
"production": {
"optimization": true,
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"vendorChunk": false,
"buildOptimizer": true
}
}
}
}
}
}
}

Conclusion

By following these practices and corrections, you can significantly improve the performance of your Angular application, making it more efficient and responsive.

Follow me on LinkedIn: https://www.linkedin.com/in/erickzanetti

--

--

Erick Zanetti
Erick Zanetti

Written by Erick Zanetti

Full Stack developer with angular and Java. Learning Go.

Responses (19)