Reader Stacks

How to Set OnPush Change Detection in an Angular Component

OnPush tells Angular to skip checking a component unless its @Input references actually changed — a genuine, measurable performance win for components in a large, frequently-updating app.

How to Set OnPush Change Detection in an Angular Component

Angular's default change detection checks every component on every application event (a click, an HTTP response, a timer) — OnPush tells a specific component to skip that automatic check unless its inputs actually changed by reference, which is a genuine, measurable performance optimization for a large or frequently-updating application.

Enabling OnPush

@Component({
    selector: 'app-product-card',
    changeDetection: ChangeDetectionStrategy.OnPush,
    template: `
{{ product.name }} — {{ product.price }}
` }) export class ProductCardComponent { @Input() product!: Product; }

Why OnPush requires immutable updates to actually work

// this does NOT trigger OnPush change detection — same object reference
this.product.price = 29.99;

// this DOES trigger it — a genuinely new object reference
this.product = { ...this.product, price: 29.99 };

OnPush compares the reference of an @Input value, not its contents — mutating an object's property in place leaves the same reference, so Angular has no signal that anything changed and won't re-render, even though the data itself is different. This is the single most common OnPush bug, and it's why OnPush components need to be paired with an immutable data-update pattern.

When OnPush change detection does run

An OnPush component is still checked when: an @Input reference genuinely changes, an event originates from within the component itself (a click handler inside it), an Observable bound with the async pipe emits a new value, or change detection is triggered manually — it isn't frozen entirely, just skipped for the unrelated, unrecognized triggers default change detection would otherwise also check it for.

Manually triggering change detection when needed

constructor(private cdr: ChangeDetectorRef) {}

updateSomethingOutsideAngularsKnowledge() {
    // e.g., a value changed via a third-party library's callback, outside Angular's zone
    this.someValue = newValue;
    this.cdr.markForCheck();
}

markForCheck() tells Angular to check this component (and its ancestors) on the next change detection cycle — necessary specifically when a value changes through a code path Angular's own change detection wouldn't automatically know to check under OnPush.

Why OnPush is a genuine performance optimization, not just a style choice

In a large application with many components, default change detection re-checking every component on every event can become a measurable performance cost — applying OnPush to components that render often but rarely actually need updating (a large list's individual row components, for instance) meaningfully reduces the total work Angular does per change detection cycle.

A reasonable strategy for adopting OnPush

Rather than converting an entire existing application to OnPush at once (which risks introducing subtle rendering bugs from the mutation issue above), applying it selectively to components that are both performance-sensitive and already follow an immutable data pattern is a more manageable, lower-risk approach.