Surgical Re-Renders, Smaller Bundles
Performance — OnPush, Signals & Beyond
The big performance wins in modern Angular: OnPush change detection, signals, trackBy in @for, lazy loading, and preloading strategies.
What you'll learn
- Switch a component to OnPush change detection
- Use track in @for to avoid unnecessary DOM churn
- Pick a router PreloadingStrategy
A few high-leverage knobs cover most Angular perf wins. Set them once and the framework does the rest.
OnPush + Signals
Default change detection re-checks every component on every event. OnPush only re-checks when inputs change or a signal it reads updates — usually a 10x speedup on large pages.
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
@Component({
selector: 'app-cart',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<p>Total: {{ total() }}</p>`,
})
export class CartComponent {
total = signal(0);
} New components should default to OnPush + signals. Treat the default strategy as legacy.
Track in @for
Without a key, Angular has to re-create every DOM node when the list changes. track lets it diff by identity.
@for (user of users(); track user.id) {
<li>{{ user.name }}</li>
} Use a stable, unique value — usually an id. $index works for static lists but loses the benefit when items move.
Lazy and Preloaded Routes
Split your app at route boundaries with loadComponent, then preload them in the background so navigation feels instant.
import { provideRouter, withPreloading, PreloadAllModules } from '@angular/router';
export const appConfig = {
providers: [
provideRouter(routes, withPreloading(PreloadAllModules)),
],
}; Measure the Bundle
ng build --stats-json
npx webpack-bundle-analyzer dist/my-app/stats.json The analyzer’s treemap shows you exactly which dependency is fat. Common offenders: full moment.js, lodash, all of RxJS. Swap them for tree-shakable equivalents (date-fns, lodash-es, specific RxJS operators).
@defer — Deferred Views →