
Intermediate workshop
Modern Angular
Master the latest Angular features to build modern applications. Learn how to use standalone components, signals, the new inject method and much more.
Learn more
The Angular team published an RFC to make OnPush the default CD (change detection) Strategy: https://github.com/angular/angular/discussions/66779. This change will be available in Angular v22.
Angular has always been a "magic-based" framework for a long time when it comes to change detection. That's because of zone.js handling most of the async operations and notifying Angular that something has changed.
@Component({
template: `
`,
})
export class AsyncClick {
count = 0;
onClick() {
setTimeout(() => {
this.count++;
}, 1000);
}
}
The last version of Angular (v21) removed that magic for new projects by making Zoneless the default choice. This means that the code above would not work as expected.

The reason for this is because nothing is listening to that setTimeout
The reason it still updates is because when we click a button Angular triggers a change detection run (same as calling
manually). But it's one update cycle too late. markForCheck
This means that we shouldn't depend anymore on zone.js notifying Angular that something has changed. We need to be explicit when we change state that shows something in the browser.
That's what signals solve! By using signals, we can tell Angular what exactly changed and the framework will take care of the rest.
@Component({
template: `
`,
})
export class AsyncClick {
count = signal(0); onClick() {
setTimeout(() => {
this.count.update((x) => x + 1);
}, 1000);
}
}
This now will work as expected. We can see the count updating in the browser as soon as the setTimeout

OnPush is what really makes Angular fast. Because it's the only way to skip change detection for a component and its children if they are not dirty (don't have changes). Signals just tell Angular what changed, while OnPush tells Angular what to skip (if not changed explicitly -> don't check).
When we update a signal that is used in the template, Angular will mark the component as dirty (RefreshViewHasChildViewsToRefresh
Because Angular until now has used a ChangeDetectionStrategy.Default
That's one of the reason why
will be renamed to ChangeDetectionStrategy.Default(meaning that change detection will be run eagerly) ChangeDetectionStrategy.Eager
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Component {}
@Component({
template: `
const userData = signal
A little bit of explanation about why direct mutation currently works:
@Component({
template: `
The reason why this works is because event listeners still mark the component as dirty. And because the component is dirty and the state is mutated, Angular will run CD and update the view. So, even though it is working, it's not the correct way to do it. Because same as before, if we put it inside a setTimeout
@Component({
template: `

By marking the signals as readonly, you will get a type error if you try to mutate the signal state directly.
const userData = signal
I'm also working to get these as linting errors here: https://github.com/angular-eslint/angular-eslint/pull/2890
When you will run ng update
It will add ChangeDetectionStrategy.Eager@Component({
template: ''
+ changeDetection: ChangeDetectionStrategy.Eager
})
export class Cmp {}
It will rename ChangeDetectionStrategy.DefaultChangeDetectionStrategy.Eager@Component({
template: ''
- changeDetection: ChangeDetectionStrategy.Default
+ changeDetection: ChangeDetectionStrategy.Eager
})
export class Cmp {}
It will keep ChangeDetectionStrategy.OnPush unchanged@Component({
template: ''
// no changes here
changeDetection: ChangeDetectionStrategy.OnPush
})
export class Cmp {}
After you migrate to v22, you can safely remove the line that sets OnPush change detection, as that would be the default.
Angular keeps getting better!

Intermediate workshop
Master the latest Angular features to build modern applications. Learn how to use standalone components, signals, the new inject method and much more.
Learn more