How Angular Knows When to Re-Render
Change Detection & OnPush
Default change detection walks the entire component tree on every async event. OnPush narrows that to just the components whose inputs or signals changed.
What you'll learn
- Understand the default Zone.js change detection
- Set OnPush via changeDetection
- See how signals make OnPush effortless
Change detection is how Angular figures out which parts of the DOM need updating. The default strategy is safe but does extra work.
The Default Strategy
Zone.js monkey-patches the browser. Whenever a click handler runs, a setTimeout fires, or a promise resolves, Angular re-checks every binding in every component. That is fine for small apps but wasteful for large ones.
OnPush
OnPush tells Angular: “Only re-check this component’s view when one of these happens.”
- An input reference changes (
===test) - An event fires inside this component
- A signal read in the template changed
- You manually call
markForCheck()
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
@Component({
selector: 'app-row',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<span>{{ user().name }}</span>`,
})
export class RowComponent {
user = input.required<{ name: string }>();
} Why Signals + OnPush Is the Sweet Spot
Mutating an object in place does not change its reference, so OnPush components miss the update. With signals, every change is an explicit .set() or .update() call, which surgically schedules the components that read that signal. No manual markForCheck, no immutable rewrites.
import { signal } from '@angular/core';
const count = signal(0);
// every component that reads count() in its template re-renders — others don't
count.update(c => c + 1); Zoneless Angular
You can drop Zone.js entirely with provideZonelessChangeDetection(). The app then relies on signals and explicit notifications. That is the direction modern Angular is heading.