Angular Labs
COOKIES
general / Use OnPush Change Detection

Use OnPush Change Detection

Optimize performance by using OnPush change detection strategy when possible

Examples

✅ Good - OnPush with Immutable Data

@Component({
selector: "app-user-list",
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div *ngFor="let user of users; trackBy: trackByUserId">
<app-user-card [user]="user" (userClicked)="onUserClicked($event)"> </app-user-card>
</div>
`,
})
export class UserListComponent {
@Input() users: User[] = [];
@Output() userSelected = new EventEmitter<User>();
trackByUserId(index: number, user: User): string {
return user.id;
}
onUserClicked(user: User): void {
this.userSelected.emit(user);
}
}

✅ Good - OnPush with Signals

@Component({
selector: "app-counter",
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div>
<p>Count: {{ count() }}</p>
<p>Double: {{ doubled() }}</p>
<button (click)="increment()">+</button>
<button (click)="decrement()">-</button>
</div>
`,
})
export class CounterComponent {
count = signal(0);
doubled = computed(() => this.count() * 2);
increment(): void {
this.count.update((value) => value + 1);
}
decrement(): void {
this.count.update((value) => value - 1);
}
}

✅ Good - OnPush with Manual Change Detection

@Component({
selector: "app-data-loader",
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div *ngIf="loading">Loading...</div>
<div *ngIf="data && !loading">
<pre>{{ data | json }}</pre>
</div>
`,
})
export class DataLoaderComponent implements OnInit {
data: any = null;
loading = false;
private cdr = inject(ChangeDetectorRef);
private httpClient = inject(HttpClient);
ngOnInit(): void {
this.loadData();
}
private loadData(): void {
this.loading = true;
this.cdr.markForCheck(); // Manual trigger
this.httpClient.get("/api/data").subscribe({
next: (response) => {
this.data = response;
this.loading = false;
this.cdr.markForCheck(); // Manual trigger
},
error: () => {
this.loading = false;
this.cdr.markForCheck(); // Manual trigger
},
});
}
}

❌ Bad - Default Change Detection (Inefficient)

@Component({
selector: "app-user-list",
// No changeDetection specified = Default strategy
template: `
<div *ngFor="let user of users">
<app-user-card [user]="user"></app-user-card>
</div>
`,
})
export class UserListComponent {
@Input() users: User[] = [];
// This will trigger change detection on every cycle
// even when users array hasn't changed
}

❌ Bad - OnPush with Mutable Operations

@Component({
selector: "app-todo-list",
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div *ngFor="let todo of todos">{{ todo.title }} - {{ todo.completed ? "Done" : "Pending" }}</div>
<button (click)="toggleFirst()">Toggle First</button>
`,
})
export class TodoListComponent {
@Input() todos: Todo[] = [];
toggleFirst(): void {
// ❌ This won't trigger change detection in OnPush
// because we're mutating the existing object
this.todos[0].completed = !this.todos[0].completed;
}
}