LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 2317 min read

Change Detection & Zone.js

Understand how Angular decides when to re-check the DOM, from classic Zone.js-based detection to modern signal-based precision.

What Is Change Detection?

Change detection is the process Angular uses to check whether any component's data has changed, and if so, update the DOM to match. Understanding roughly how it works helps explain both classic Angular's performance characteristics and why Signals were introduced.

How Zone.js Triggers It

By default, Angular includes Zone.js, a library that patches common async browser APIs (setTimeout, Promise callbacks, DOM events) so Angular is notified whenever one of them completes. After any such event, Angular re-checks the entire component tree from the root, comparing each binding's current value to its last-known value.

An async event fires (click, timer, HTTP response)
↓
Zone.js notifies Angular
↓
Angular re-checks every component from the root
↓
Bindings that changed update the DOM

ChangeDetectionStrategy.OnPush

Setting a component's change detection strategy to OnPush tells Angular to skip re-checking it unless one of its @Input() references actually changed (or an event originated from within it) — a common, effective optimization for components deep in a large tree.

import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
@Component({
selector: 'app-product-card',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<h3>{{ product.name }}</h3>`,
})
export class ProductCardComponent {
@Input() product!: { name: string };
}
OnPush Needs Immutable Updates

With OnPush, mutating an object's property in place (product.name = 'New') won't trigger a re-check — you need to pass a genuinely new object reference for Angular to notice the change.

How Signals Change the Picture

Signals sidestep this whole broad-recheck model. Because a signal knows exactly which template bindings read it, Angular can update just those specific bindings directly, without walking the rest of the component tree at all — a fundamentally more precise (and, for large apps, more scalable) approach.

Zone.js (Classic)Signals
TriggerAny patched async API completingA specific signal being written to
Scope of recheckThe whole component tree (unless OnPush)Only bindings that actually read that signal
Requires Zone.js?YesAngular can run zoneless with signals (an evolving capability)

Common Beginner Mistakes

Mutating an @Input() object in place on an OnPush component

Angular compares references, not deep equality — always create a new object/array when the data genuinely changed, so OnPush components notice.

Assuming signals eliminate the need to think about change detection entirely

They dramatically reduce how often you need to reason about it, but understanding the underlying model still helps when debugging an unexpected stale (or overly frequent) update.

FAQs

It's a good default habit for presentational components fed entirely by inputs, but always update input data immutably if you do — otherwise UI can silently fail to refresh.

Angular has been moving toward optional, signal-driven "zoneless" change detection — check current Angular release notes for the latest state of this transition.

Summary

Classic Angular re-checks broadly via Zone.js, OnPush narrows that scope for immutable-input components, and Signals enable the most precise updates of all. With standalone components and Signals covered, the next section moves into services and dependency injection.

Next Lesson →

Services & Dependency Injection