Interview answer drill

Use this Angular interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.

Explain OnPush Change Detection in Angular Like You’re Debugging a Real Production BugFrontend interview answer

HighIntermediateAngular
Interview focus

This Angular interview question tests whether you can explain Angular OnPush in production: triggers, stale-UI bugs, and markForCheck debugging, connect it to production trade-offs, and handle common follow-up questions.

  • Angular OnPush in production: triggers, stale-UI bugs, and markForCheck debugging explanation without falling back to memorized docs wording
  • Change Detection and Onpush reasoning, edge cases, and production failure modes
  • How you would answer the most likely Angular interview follow-up
Practice more Angular interview questions
Interview quick answer

OnPush change detection is easiest to explain as a real stale-UI debugging story. Angular skips a component unless a narrow trigger happens, so production bugs usually come from mutating references, updating state inside manual subscriptions, or misunderstanding parent-child boundaries.

Full interview answer

Core idea

ChangeDetectionStrategy.OnPush tells Angular: “Only check this component if something meaningful happened.” Meaningful = an @Input reference changed, an event happened in this component, an async pipe emitted, or you manually asked Angular to check.

What happened?

Will OnPush re-render?

Why

An @Input reference changed

✅ Yes

Angular compares references, not deep values.

A click/event inside the component

✅ Yes

Events mark the component dirty.

An async pipe emits a new value

✅ Yes

AsyncPipe internally calls markForCheck().

You mutated an object/array in place

❌ No

Reference did not change, Angular thinks nothing happened.

You call markForCheck() manually

✅ Yes

You explicitly told Angular to re-check.

You call detectChanges() manually

✅ Yes (immediate)

Forces synchronous change detection for this subtree.

What triggers change detection in OnPush components?

The classic real-world bug

“My data changes but the UI doesn’t update.” This almost always means you mutated an object or array instead of creating a new reference.

TYPESCRIPT
@Component({
  selector: 'app-user-list',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div *ngFor="let user of users">{{ user.name }}</div>
  `
})
export class UserListComponent {
  @Input() users: { name: string }[] = [];

  // ❌ BUG: UI will NOT update in OnPush
  addUserWrong() {
    this.users.push({ name: 'New User' }); // same array reference
  }

  // ✅ Correct: new reference => OnPush sees change
  addUserCorrect() {
    this.users = [...this.users, { name: 'New User' }];
  }
}
                  

Why OnPush exists

Default change detection checks the whole component tree on every async event (timers, HTTP, clicks, etc). OnPush lets Angular skip huge parts of the tree unless something relevant changed. This is critical for large apps and big lists.

Second real bug variant: manual subscription updates state, but the view still looks stale

Even if no input mutation happens, an OnPush component can still miss updates when you subscribe imperatively and update local state without async pipe or markForCheck(). This is the other classic production bug after in-place mutation.

TYPESCRIPT
@Component({
  selector: 'app-summary-card',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `{{ stats.total }}`
})
export class SummaryCardComponent {
  stats = { total: 0 };

  constructor(
    private statsService: StatsService,
    private cd: ChangeDetectorRef
  ) {
    this.statsService.total$.subscribe(total => {
      // ❌ Bug: same object reference + no explicit mark
      this.stats.total = total;
    });
  }

  connectSafely() {
    this.statsService.total$.subscribe(total => {
      this.stats = { ...this.stats, total };
      this.cd.markForCheck();
    });
  }
}
                  

markForCheck() vs detectChanges()

Method

What it does

When to use

markForCheck()

Marks this component and its parents as dirty for the next CD cycle.

When data changes outside Angular's awareness (e.g., custom subscriptions, timers, non-async-pipe streams).

detectChanges()

Immediately runs change detection on this component subtree.

Rare cases: manual rendering, detached views, or very controlled performance scenarios.

Manual change detection tools

AsyncPipe is your best friend

The async pipe automatically:
• Subscribes
• Unsubscribes
• Calls markForCheck() on emission

This is why OnPush + async pipe is the recommended default pattern.

Common senior-level pitfalls

• Mutating arrays/objects instead of replacing them
• Forgetting that OnPush uses reference checks, not deep checks
• Updating state inside subscriptions without async pipe or markForCheck
• Assuming a parent mutating child.input.deep.nestedValue should refresh the child even though the input reference stayed the same
• Using OnPush everywhere without understanding its mental model

Scenario

What to do

Why

State comes from Observables

Use async pipe

Automatic CD + auto unsubscribe + best performance.

State updated manually in code

Use immutable updates (new reference)

So OnPush can detect the change.

State updated outside Angular or in manual subscriptions

Call markForCheck()

Otherwise Angular will not re-render.

Correct patterns with OnPush

How to debug it in a real app

Use Angular DevTools or a quick logging pass to verify which component is actually being checked. If the parent rerenders but the child does not, look for an unchanged input reference. If neither rerenders after a subscription callback, check whether the update happened outside the normal template flow and needs markForCheck() or an async pipe.

Interview-level summary

OnPush makes Angular check a component only when inputs change by reference, an event happens, an async pipe emits, or you manually trigger detection. It gives big performance wins, but requires immutable state updates. Most “UI not updating” bugs in OnPush come from mutating objects instead of replacing them.

Similar questions
Guides
Preparing for interviews?

Use this as one explanation rep, then continue with the Angular interview questions cluster or a guided prep path.