1. What is ChangeDetectionStrategy.OnPush?
What is it?
ChangeDetectionStrategy.OnPush is an Angular change-detection strategy that allows Angular to skip checking a component subtree when Angular has no indication that the subtree needs to be refreshed.
It is mainly used to reduce unnecessary change-detection work and improve performance, particularly in larger applications.
changeDetection: ChangeDetectionStrategy.OnPush
The simple idea is:
Default
↓
Angular normally checks the component
during change-detection traversal
OnPush
↓
Angular can skip the component subtree
unless it needs checking
Full Simple Sample Program
This example demonstrates an important OnPush concept: input reference changes.
app.ts — Parent
import { Component } from '@angular/core';
import { UserCard } from './user-card';
@Component({
selector: 'app-root',
standalone: true,
imports: [UserCard],
template: `
<h1>OnPush Example</h1>
<app-user-card [user]="user"></app-user-card>
<button (click)="changeUser()">
Change User
</button>
`
})
export class App {
user = {
name: 'Syed'
};
changeUser(): void {
this.user = {
name: 'Mohamed'
};
}
}
user-card.ts — Child
import {
ChangeDetectionStrategy,
Component,
Input
} from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
template: `
<h2>User: {{ user.name }}</h2>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserCard {
@Input() user!: { name: string };
}
How Does It Work?
Initially:
user = {
name: 'Syed'
};
The child receives:
user object
↓
User: Syed
When we click:
<button (click)="changeUser()">
the parent executes:
this.user = {
name: 'Mohamed'
};
Notice that we created a new object.
Old object reference
↓
{ name: 'Syed' }
New object reference
↓
{ name: 'Mohamed' }
The child's input reference has changed.
Angular can therefore check the OnPush child, and the UI becomes:
User: Mohamed
Important OnPush Problem: Mutating an Object
Suppose instead we write:
changeUser(): void {
this.user.name = 'Mohamed';
}
We changed a property, but the user object itself is still the same reference.
Before:
user ──────► Object A
name = Syed
After:
user ──────► Object A
name = Mohamed
Same Object A
For an OnPush child receiving that object as an input, this kind of mutation can prevent the input change itself from causing the child to be checked.
That is why immutable updates are a common pattern with OnPush.
Prefer:
this.user = {
...this.user,
name: 'Mohamed'
};
Now:
Old:
user ──────► Object A
New:
user ──────► Object B
The reference changed.
When Can an OnPush Component Be Checked?
A common oversimplification is:
OnPush checks only when
@Inputchanges.
That's not correct.
Important situations include:
1. Input changes
this.user = {
name: 'Mohamed'
};
A new input reference/value can cause the OnPush component to be checked.
2. An event occurs in the component
<button (click)="increment()">
Increment
</button>
An event handled in the component/subtree can cause it to be checked.
3. A Signal used by the template changes
count = signal(0);
increment() {
this.count.update(x => x + 1);
}
Angular tracks the signal dependency and knows the consuming view needs refreshing.
4. AsyncPipe receives a new value
For example:
<p>{{ user$ | async }}</p>
AsyncPipe marks the view for checking when a new value arrives.
5. The component is manually marked
For example:
changeDetectorRef.markForCheck();
Advantages of OnPush
OnPush can:
- Reduce unnecessary component checks
- Improve performance in large component trees
- Work well with immutable state
- Work well with Signals
- Work well with Observables and
AsyncPipe - Encourage predictable state-management patterns
Key Points
OnPushis an Angular change-detection strategy.- It allows Angular to skip unnecessary component subtrees.
- It is not correct to say "
OnPushchecks only when@Inputchanges." - Input reference changes are particularly important for objects and arrays.
- Avoid directly mutating input objects when relying on immutable
OnPushpatterns. - Signals work very naturally with
OnPush.
2. Default vs OnPush Change Detection — What Is the Difference?
The main difference is how aggressively Angular checks component subtrees during change detection.
Default
changeDetection: ChangeDetectionStrategy.Default
Angular uses normal eager checking when traversing the component tree.
OnPush
changeDetection: ChangeDetectionStrategy.OnPush
Angular can skip an OnPush component subtree when it has no reason to believe the subtree needs refreshing.
Simple Comparison
| Feature | Default | OnPush |
|---|---|---|
| Checking behavior | Eager/normal | Can skip unaffected subtrees |
| Performance optimization | Less aggressive | More aggressive |
| Object mutation | Often appears to work | Can cause stale child UI |
| Immutable objects | Optional | Strongly recommended |
| Signals | Supported | Excellent combination |
AsyncPipe |
Supported | Excellent combination |
| Ease of use | Simpler | Requires better state discipline |
| Large applications | Works | Often useful for optimization |
Example
Imagine:
App
│
├── Header
│
├── ProductList
│ │
│ ├── Product 1
│ ├── Product 2
│ └── Product 3
│
└── Footer
With normal Default checking, Angular performs its standard eager traversal when change detection runs.
Conceptually:
Change Detection
↓
App
↓
┌────┼────────┐
↓ ↓ ↓
Header Products Footer
↓
Products
With OnPush, Angular can skip subtrees that don't need checking:
Change Detection
↓
App
↓
┌────┼─────────┐
↓ ↓ ↓
skip Products skip
This becomes valuable when an application has hundreds or thousands of components.
Very Important: Primitive vs Object Inputs
This is one of the most important OnPush interview concepts.
Primitive
name = 'Syed';
Then:
this.name = 'Mohamed';
The value changed:
Syed → Mohamed
Straightforward.
Object
user = {
name: 'Syed'
};
Don't rely on mutating it:
this.user.name = 'Mohamed';
The object reference remains the same.
Prefer creating a new object:
this.user = {
...this.user,
name: 'Mohamed'
};
Now the reference changes.
Same idea with arrays.
Avoid:
this.products.push(newProduct);
Prefer:
this.products = [
...this.products,
newProduct
];
Conceptually:
Mutation
Array A
↓
push()
↓
Array A
Same reference
versus:
Immutable update
Array A
↓
create new array
↓
Array B
New reference
Default vs OnPush — Easy Way to Remember
Think of Default as:
"Check normally whenever Angular performs its change-detection traversal."
Think of OnPush as:
"Skip my subtree unless Angular knows I need checking."
So:
DEFAULT
========
Change Detection
↓
Check normally
↓
Evaluate bindings
↓
Update DOM if required
ONPUSH
======
Change Detection
↓
Does this subtree need checking?
↓
┌──┴──┐
No Yes
↓ ↓
Skip Check
↓
Evaluate bindings
↓
Update DOM if required
Key Points
- Angular supports Default and OnPush change-detection strategies.
Defaultuses Angular's normal eager checking behavior.OnPushenables Angular to skip unaffected component subtrees.OnPushdoes not mean "Angular checks only once."OnPushdoes not mean "Angular checks only when@Inputchanges."- Object and array reference changes are important with
OnPush. - Immutable state updates are a good practice with
OnPush. - Signals and
OnPushwork particularly well together. AsyncPipeworks correctly withOnPush.- Use
markForCheck()when you need to explicitly mark anOnPushview for checking.
Interview Questions and Answers
1. What is ChangeDetectionStrategy.OnPush?
OnPush is an Angular change-detection strategy that allows Angular to skip a component subtree when Angular has no indication that the subtree needs checking.
2. What is the difference between Default and OnPush?
Default uses normal eager checking during change-detection traversal.
OnPush allows Angular to skip unaffected component subtrees, reducing unnecessary work.
3. Does OnPush mean Angular checks only when @Input changes?
No. That's a common misconception.
An OnPush component can also be checked because of events, Signals, AsyncPipe, markForCheck(), and other Angular notification mechanisms.
4. Why are immutable objects recommended with OnPush?
Because OnPush commonly relies on input value/reference changes. Mutating an existing object preserves its reference, whereas creating a new object provides a new reference.
// Mutation
this.user.name = 'Mohamed';
// Immutable update
this.user = {
...this.user,
name: 'Mohamed'
};
5. Does OnPush work with Signals?
Yes. Signals work very well with OnPush.
count = signal(0);
count.update(x => x + 1);
When a template-consumed Signal changes, Angular knows the consuming view needs refreshing.
6. Which is faster: Default or OnPush?
OnPush can reduce change-detection work because Angular can skip unaffected component subtrees. The actual performance benefit depends on the application's component tree and update patterns.
7. Should every component use OnPush?
Not necessarily. OnPush is especially useful where predictable state flow, Signals, Observables, immutable data, or large component trees make subtree skipping beneficial.
Interview Quick Answer
Default change detection uses Angular's normal eager checking behavior, while
OnPushallows Angular to skip component subtrees that do not need checking.OnPushworks especially well with immutable inputs, Signals, and Observables, and can reduce unnecessary change-detection work.