All topics
Performanceintermediate

Change Detection Strategy Fundamentals

Explain the difference between Default and OnPush change detection strategies and why OnPush is a key performance lever.

Angular's Default change detection strategy checks every component in the tree on every change detection cycle, which is simple and safe but wasteful: most components' data hasn't actually changed most of the time, yet Angular still re-evaluates their template bindings just in case. ChangeDetectionStrategy.OnPush is the single most impactful, broadly-applicable performance optimization in Angular's component model, and it's a near-guaranteed interview topic in any performance-focused discussion.

Default change detection is like a security guard who walks every single hallway in a large building every five minutes regardless of any actual activity; OnPush is like installing motion sensors that only alert the guard to check a specific hallway when something has actually triggered them.

Key Concepts

1
An OnPush component tells Angular it only needs to be re-checked when one of a specific, limited set of triggers occurs: one of its @Input() properties receives a new reference (not just a mutated property on the same object — reference equality matters enormously here), an event originating from within the component (or its children) fires, an Observable bound via the async pipe emits a new value, or a Signal the component reads changes. Outside of these triggers, Angular skips checking that component and its entire subtree entirely during a given change detection cycle, which can meaningfully reduce work in a large application with many components.
OnPush@Input()
2
This is exactly why immutability matters so much in an OnPush-heavy Angular application: if a parent mutates an array in place (items.push(newItem)) and passes the same array reference down to an OnPush child, the child never re-renders, since Angular only compares by reference — the fix is always creating a new reference (items = [...items, newItem]) when the intent is for bound children to notice the change.
OnPushitems.push(newItem)items = [...items, newItem]
3
A well-rounded interview answer notes that Signals make this optimization essentially automatic and much harder to get wrong: because Signal-based state tracks fine-grained dependencies directly rather than relying on reference-equality-based @Input() checks, components built primarily around Signals benefit from OnPush-like efficiency without the developer needing to carefully manage object immutability by hand the way traditional OnPush components require.
@Input()