Why does an OnPush Angular child not update when its input changes?

Tests OnPush reference equality and manual change detection. Strong answers name: in-place object mutation as the root cause; ChangeDetectorRef.markForCheck or detectChanges to force updates; and Angular Signals as a modern path.
WHAT THIS TESTS: This question probes whether you understand Angular's component-level change detection strategy and the difference between reference and value equality in JavaScript. OnPush is designed for performance, and misusing it by mutating state is one of the most common production bugs in Angular applications. The interviewer wants to hear that you know when Angular skips checks and how to force them correctly without destroying performance.
A GOOD ANSWER COVERS: First, state that OnPush components run change detection only when a new reference is passed to an Input, an event binding fires from the component or its children, or an async pipe receives a new emission. Second, identify the root cause: the parent mutated an object or array in place, such as calling push or assigning a property directly, so the input reference never changed and Angular's SimpleChanges check found nothing. Third, mention edge cases where code runs outside NgZone, such as some third-party callbacks or WebSocket handlers, which also prevents automatic detection. Fourth, explain the two programmatic fixes: ChangeDetectorRef.markForCheck marks the component and its ancestors for checking in the next change detection cycle, which is the preferred light-touch fix for OnPush, while ChangeDetectorRef.detectChanges runs change detection immediately on the component and its descendants, which is heavier but useful when you need synchronous UI updates. Fifth, note that Angular v22 Signals offer a modern alternative where signal-based inputs trigger updates automatically through fine-grained reactivity rather than reference checks.
COMMON WRONG ANSWERS: Saying that OnPush performs deep comparison on object properties. Recommending ApplicationRef.tick as the first solution, which forces global change detection and defeats the purpose of OnPush. Confusing markForCheck with detectChanges by claiming both execute immediately. Suggesting to switch the child back to Default change detection as the primary fix, which signals you do not understand performance optimization. Mentioning setInterval or setTimeout without explaining zone implications.
LIKELY FOLLOW-UPS: How would you refactor this component to use signals instead of manual change detection triggers? When is detectChanges actually necessary versus markForCheck? How do you enforce immutability across a large team?
ONE CONCRETE EXAMPLE: Imagine a parent component holds an array of orders and passes it to an OnPush child grid. If the parent calls orders.push(newOrder), the grid never updates because the array reference is identical. The correct fix is to create a new reference with orders = [...orders, newOrder], which triggers OnPush. If the child still needs an update after an internal async fetch, you inject ChangeDetectorRef and call markForCheck. In Angular v22, you might instead define the input as a signal and update it with orders.update(current => [...current, newOrder]), letting the signal notify subscribers directly.
Source: angular.dev
Read the original → angular.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.