Chapter 12.1โ˜• 15 min read

Change Detection โ€” Default vs OnPush

Angular's change detection mechanism decides when to update the template. Default checks everything. OnPush checks only when needed. Signals make OnPush effortless.

01What is Change Detection

Change detection is Angular's mechanism to check if the template needs updating. It compares current component values with previous values and re-renders if anything changed.

"Change detection = teacher checking homework โ€” kuch badla hai toh mark karo."

What triggers change detection?

  • Browser events (click, keyup, submit)
  • Timers (setTimeout, setInterval)
  • HTTP responses
  • Promises resolving
  • Any async operation (Zone.js catches them all)

Angular runs change detection after every async operation. The question is HOW MANY components get checked each time.

// Every click triggers change detection
@Component({...})
export class AppComponent {
  onClick() {
    // After this runs, Angular checks ALL components
    console.log('Click handled');
  }
}
02Default Strategy โ€” Check Everything

ChangeDetectionStrategy.Default is the default behavior. When any async operation happens ANYWHERE in the app, Angular checks EVERY component in the component tree.

import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'app-slow',
  template: `

{{ data }}

`, changeDetection: ChangeDetectionStrategy.Default // Default (or omit) }) export class DefaultComponent { ... }

"Default = har baar sabke ghar check karo โ€” safe but slow."

How it works:

  • User clicks a button in Component Z
  • Angular runs change detection on ALL 100 components
  • Even components unrelated to the click get checked
  • Every check: compare previous vs current values
  • 100 components ร— 10 bindings = 1000 comparisons per click

When Default is fine: Small apps (<20 components), simple pages, prototypes. For larger apps, Default becomes a performance bottleneck.

03OnPush Strategy โ€” Check Only When Needed

ChangeDetectionStrategy.OnPush tells Angular to check a component ONLY when specific conditions are met โ€” not automatically on every async operation.

import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'app-fast',
  template: `

{{ data() }}

`, changeDetection: ChangeDetectionStrategy.OnPush // Smart! }) export class FastComponent { ... }

"OnPush = smart check โ€” sirf jab zaroor ho tab check karo."

Performance difference:

  • Default: 100 components checked on EVERY async operation
  • OnPush: component checked ONLY if its input/signal/event changed
  • Typical app: click affects 3-5 components, not all 100
  • OnPush makes large apps 10-20x faster

With signals, OnPush is even better: Signals tell Angular EXACTLY which components to check. No tree traversal, no comparison โ€” just direct notification to the affected components.

04OnPush Rules โ€” What Triggers Check

OnPush is strict about what triggers a check. Here's what DOES and DOESN'T work:

TriggerOnPush check?
@Input receives NEW object referenceโœ… Yes
@Input object MUTATED (same reference)โŒ No
Component's own event (click, input)โœ… Yes
Async pipe receives new valueโœ… Yes
Signal value changesโœ… Yes (Angular 17+)
setTimeout / setIntervalโŒ No (unless markForCheck)
Service BehaviorSubject emitsโŒ No (unless async pipe)
Manual: cdr.markForCheck()โœ… Yes

"OnPush = strict bouncer โ€” sirf valid reasons pe andar jaane do."

// This DOES trigger OnPush:
@Input() items: Item[];  // Parent passes new array reference

// This DOES NOT trigger OnPush:
this.items.push(newItem);  // Same array reference

// Solution: always create new reference:
@Input() items = signal([]);
this.items.update(list => [...list, newItem]);  // New reference!
05When to Use OnPush

Best practice for Angular 17+:

import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'app-modern',
  template: `

{{ count() }}

`, changeDetection: ChangeDetectionStrategy.OnPush // Default for all new code }) export class ModernComponent { count = signal(0); // With signals, OnPush is effortless }

"Default = training wheels, OnPush = real cycling โ€” start with OnPush."

Rule: OnPush for EVERY component by default

  • New Angular apps: set OnPush globally or per-component
  • With signals, OnPush adds ZERO extra work โ€” signals auto-trigger checks
  • Without signals, OnPush requires you to be careful about immutability
  • Default strategy is only needed for legacy code that mutates data

Migration path:

  1. Add signal() for component state
  2. Set changeDetection: OnPush
  3. Replace @Input mutations with immutable patterns
  4. Replace subscribe() with toSignal() or async pipe

Key Takeaways

  • โœ… Change detection checks if template needs update after async operations
  • โœ… Default strategy: checks EVERY component on every async event
  • โœ… OnPush strategy: checks ONLY when inputs/events/signals change
  • โœ… OnPush triggers: new @Input ref, component event, signal, async pipe
  • โœ… OnPush does NOT trigger: mutation, setTimeout, service emission alone
  • โœ… Use OnPush by default โ€” signals make it effortless and powerful
Course Search
Search across all chapters & stages
๐Ÿ“–

Search the course

Type any topic โ€” branching, stash, rebase, hooks โ€” and jump straight to that chapter.

merge branchesgit stashundo commitrebase