Default Change Detection
Angular's default change detection walks the entire component tree on every change detection cycle, checking all bindings for changes.
How Default Change Detection Works
- An event occurs (click, HTTP response, timer, etc.)
- Angular runs change detection from the root component
- Every component's template bindings are checked
- If any binding changed, the DOM is updated
@Component({
selector: 'app-example',
template: `<p>{{ message }}</p>`,
// changeDetection: ChangeDetectionStrategy.Default // default
})
export class ExampleComponent {
message = 'Hello';
}
When Default Detection Runs
- DOM events (click, input, etc.)
- HTTP responses (subscribe)
- Timers (setTimeout, setInterval)
- Promise resolutions
- Manual markForCheck() calls
Performance Implications
For deep component trees, default change detection can be expensive because it checks every binding in every component, even if nothing changed.
OnPush Change Detection
OnPush components only re-render when their input references change, a signal changes, or an event originates from the component.
Enabling OnPush
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-user-card',
template: `<p>{{ user.name }}</p>`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UserCardComponent {
@Input() user!: User;
}
When OnPush Components Re-render
- Input reference changes — New object/array reference
- Signal changes — Any signal read in the template updates
- Event originates from the component — (click) handler triggers detection
- markForCheck() is called — Manually marks the component for checking
// BAD: Mutating an object does NOT trigger OnPush
this.user.name = 'New Name'; // Same reference — no update
// GOOD: New reference triggers OnPush
this.user = { ...this.user, name: 'New Name' };
markForCheck
import { ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'app-example',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ExampleComponent {
private cdr = inject(ChangeDetectorRef);
data: any;
loadData(): void {
this.dataService.getData().subscribe(data => {
this.data = data;
this.cdr.markForCheck(); // Trigger change detection
});
}
}
Default vs OnPush
| Feature | Default | OnPush |
|---|---|---|
| Re-renders | On every change detection cycle | Only on input/signal/event changes |
| Performance | Slower for deep trees | Much faster |
| Immutability | Not required | Required for inputs |
| Use for | Simple components | Performance-critical components |
Signals and OnPush
Signals work with both default and OnPush strategies. A signal read in a template automatically triggers re-render when the signal changes:
@Component({
selector: 'app-counter',
template: `<p>{{ count() }}</p>`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CounterComponent {
count = signal(0);
increment(): void {
this.count.update(c => c + 1); // Triggers re-render
}
}
Quiz
1. When does an OnPush component re-render?
2. Why does mutating an object not trigger OnPush?
3. What does markForCheck() do?
Flashcards
Question
When does OnPush re-render?
Click to reveal answer
Answer
Input reference change, signal change, component event, or markForCheck().
Question
Why does mutation not trigger OnPush?
Click to reveal answer
Answer
OnPush checks reference equality. Mutations keep the same reference.
Question
What does markForCheck() do?
Click to reveal answer
Answer
Flags OnPush ancestor for checking in next change detection cycle.
Revision Notes
Key Takeaways
- 1. Default change detection walks the whole tree
- 2. OnPush only re-renders on input/signal/event changes
- 3. OnPush requires immutable inputs
- 4. markForCheck() manually triggers OnPush updates
Interview Tips
- • Explain the performance benefits of OnPush
- • Be ready to describe immutable data patterns
- • Mention signals as fine-grained reactivity
Cheat Sheet
Change Detection Cheat Sheet
Default:
- Re-renders on every change detection cycle
- Checks all bindings in all components
OnPush:
changeDetection: ChangeDetectionStrategy.OnPush
- Re-renders only on input/signal/event changes
- Requires immutable inputs
markForCheck:
this.cdr.markForCheck();
Key rule:
- OnPush + mutation = no update
- OnPush + new reference = update