Using Angular's ChangeDetectionStrategy.OnPush to Reduce Unnecessary Checks
Learn how Angular's ChangeDetectionStrategy.OnPush cuts unnecessary change detection cycles by updating views only when input references change or events fire, with a concrete component example and common pitfalls.
05 Sept 2025, 15:54 UTC

Why Use ChangeDetectionStrategy.OnPush?
Angular runs change detection on every component tree whenever an asynchronous event occurs (e.g., a click, timer, or HTTP response). In large applications this can cause many unnecessary checks and hurt performance. By setting ChangeDetectionStrategy.OnPush on a component, you tell Angular to skip its subtree unless one of three conditions is met:
- One of the component’s @Input() bindings receives a new reference (i.e., the object/array changed identity).
- An event originated from the component or its children (click, submit, etc.).
- An observable bound in the template emits a value (usually via the async pipe).
Worked Example: ItemComponent with OnPush
The following component displays a single item’s name. It declares OnPush so Angular will only check it when the item input reference changes or when an event occurs inside the component.
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
@Component({
selector: 'app-item',
template: '<p>{{ item.name }}</p>',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ItemComponent {
@Input() item: any;
}
Parent Template Using the Async Pipe
In the parent component we expose an Observable of the item data. The async pipe subscribes automatically and passes the latest emitted value to app-item. Because the async pipe returns a new reference each time the observable emits, OnPush treats this as a change and updates the view.
<app-item [item]='item$ | async'></app-item>
Limits and Common Mistakes
- Mutating input objects – If you change a property of the
itemobject directly (e.g.,item.name = 'Updated') the reference stays the same, so OnPush does not trigger a view update. The UI will show stale data until another trigger occurs. - Missing async pipe – Passing the raw Observable (
[item]='item$') without the async pipe gives the component the Observable instance, not the emitted value. The view never updates because the reference never changes. - Need for manual check – When you must mutate an input object, you can force a check by injecting
ChangeDetectorRefand callingmarkForCheck()after the mutation.
Practical Verification
You can confirm the effect of OnPush by comparing change detection cycles with and without the strategy:
- Run the app with
ng serve. - Open Chrome DevTools console and execute
ng.profiler.timeChangeDetection(); note the reported time. - Repeat the measurement after removing
changeDetection: ChangeDetectionStrategy.OnPushfrom the component. - Typically you will see a lower time (fewer cycles) when OnPush is active, especially if the parent triggers frequent events that do not change the
itemreference.
Alternatively, mutate an input object inside a button handler and observe that the view does not refresh until you call markForCheck() or interact with the component in another way that triggers change detection.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.