Angular Change Detection Cheat Sheet
-
Stefanos Lignos - Modified Fr Aug, 2025
The need for Reactivity
Reactivity is a design paradigm that refers to a system’s ability to respond to changes in data or state automatically.
All modern front-end frameworks have implemented their own versions of reactivity so developers don’t have to worry about synchronizing changes in the state or data (Model) with the UI (View).
A few years ago, Angular adopted Zone.js as the foundational tool for its implementation of reactivity. The following paragraphs serve as a cheat sheet explaining how reactivity works in Angular today using Zone.js, how it has evolved in recent years into a more fine-grained reactivity model with Signals, and finally, how reactivity functions without Zone.js.
In Angular, we traditionally refer to this process of keeping the View in sync with the Model as Change Detection.
How Change Detection works
Default Change Detection
An Angular app might consist of several components (in most cases, hundreds or even thousands in enterprise applications). These components form a structure known as the component tree. Angular allows us to specify two modes of change detection strategies in our components. When the Default strategy is selected, a change detection cycle always starts from the root component of this tree and traverses it in a depth-first order, visiting every node. For each node it visits, a number of actions are performed to ensure that changes in the Model(state/data) are reflected in the View. In most cases, this is a fast process. However, there are situations where change detection can become a performance bottleneck for an app.
OnPush Change Detection
By setting the OnPush strategy for our components, we can exclude branches of the component tree during the change detection traversal. When a component uses the OnPush change detection strategy, the component itself and its children are checked for changes only when they are marked as “dirty” under one of the following scenarios:
- One of its
@Inputproperties changes (Angular compares the values of@Inputproperties by reference). - An event is emitted from the component.
- The
ChangeDetectorRef.markForCheckfunction is called manually. - An Observable consumed by an
asyncpipe in the template emits a new value.
The markForCheck function marks the component itself and all of its ancestors in the component tree as “dirty,” running recursively from the component up to the root component.
When is a Change Detection Triggered?
Angular relies on a library called Zone.js to invoke a change detection cycle. Zone.js monkey-patches more than 250 browser APIs (e.g., XHR requests, DOM events, setInterval). Angular loads Zone.js and creates a zone called NgZone. NgZone contains an onMicroTaskEmpty observable, which Angular subscribes to. It triggers change detection when the microtask queue is empty by calling AppRef.tick().
Reactivity model using Signals in Angular
Signals is the reactivity model selected by the Angular team as a replacement for the existing model using NgZone.
A Signal is a wrapper around a value that notifies interested consumers when the value changes.
The Producer/Consumer Abstraction
The reactivity model in Angular using Signals is based on two abstractions: Producers and Consumers.
Consumersrepresentreactive contexts(tracking scopes).Producersrepresent entities that hold a value. Changes to this value can be tracked only when the producer is accessed within a consumer’s reactive context.
In this sense, we can think of consumers and producers as interconnected nodes, forming a dependency graph that describes the reactive behavior of a system. In an Angular component, the View acts as a consumer. When a producer, like a Signal, is accessed within the View, the consumer begins tracking it. When the value of the Signal changes, the reactive consumer of the template is notified. In that case, two things happen:
- The View is marked with the
RefreshViewflag. - The
markAncestorsForTraversalfunction is called, marking all ancestors of the View with theHasChildViewsToRefreshflag.
Here is a comparison of how change detection behaves when a signal is used in a template versus when an observable with an async pipe is used.

Global/Targeted Change Detection
When Zone.js triggers a change detection cycle, Angular traverses the component tree from top to bottom, as described earlier. However, this traversal always happens in one of two modes: Global or Targeted.
In an Angular app using Zone.js, the traversal of the component tree begins in Global mode by default. In this mode, as previously discussed:
- A component is always checked when its change detection strategy is
Default. - A component with the
OnPushstrategy is checked only if it is marked asdirty. - When Angular encounters a
non-dirtycomponent with theOnPushstrategy, it switches toTargetedmode. In this mode, Angular will:
- Visit components but will not perform change detection for components with the
Defaultstrategy or those marked asdirty + OnPush. - Perform change detection only for components marked with the
RefreshViewflag. - Switch back to
Globalmode when it reaches a component marked with theRefreshViewflag.
