Angular Default vs OnPush Change Detection

Learn Angular Default vs OnPush Change Detection with architecture diagrams, performance comparison, Signals integration, ChangeDetectorRef, enterprise use cases, best practices, and interview questions.

Angular Default vs OnPush Change Detection - Complete Guide

Choosing the correct Change Detection Strategy is one of the most important performance decisions in Angular applications.

Angular provides two built-in strategies:

  • Default
  • OnPush

Understanding the differences between them helps developers build faster, more scalable, and enterprise-ready applications.

In modern Angular, Signals integrate seamlessly with both strategies and further improve rendering performance.


Table of Contents

  • What is Change Detection?
  • Default Strategy
  • OnPush Strategy
  • Architecture Comparison
  • Default vs OnPush
  • Signals with OnPush
  • ChangeDetectorRef
  • Enterprise Banking Example
  • Performance Best Practices
  • Common Mistakes
  • Top 10 Interview Questions
  • Summary

What is Change Detection?

Change Detection is Angular's mechanism for keeping the DOM synchronized with application state.

Whenever application data changes, Angular determines which bindings must be refreshed and updates the UI.


Change Detection Overview

flowchart LR

UserEvent

APIResponse

SignalUpdate

Timer

UserEvent --> ChangeDetection

APIResponse --> ChangeDetection

SignalUpdate --> ChangeDetection

Timer --> ChangeDetection

ChangeDetection --> ComponentTree

ComponentTree --> DOM

Default Change Detection Strategy

The Default strategy is Angular's standard behavior.

@Component({

selector: 'app-dashboard'

})
export class DashboardComponent {

}

Angular checks:

  • Current component
  • Child components
  • Entire affected component tree

during every Change Detection cycle.

Advantages

  • Easy to understand
  • Less developer effort
  • Great for beginners
  • Suitable for small applications

Disadvantages

  • More component checks
  • Higher CPU usage
  • Less efficient in very large applications

Default Strategy Flow

flowchart TD

Application

Application --> Root

Root --> ComponentA

Root --> ComponentB

ComponentA --> ChildA

ComponentB --> ChildB

Root --> Checked

ComponentA --> Checked

ComponentB --> Checked

ChildA --> Checked

ChildB --> Checked

OnPush Change Detection Strategy

OnPush minimizes unnecessary checks.

import {

Component,
ChangeDetectionStrategy

} from '@angular/core';

@Component({

selector:'app-user',

changeDetection:

ChangeDetectionStrategy.OnPush

})
export class UserComponent{

}

Angular checks an OnPush component only when one of these occurs:

  • An Input reference changes
  • An event originates from the component or one of its children
  • An Observable bound with the Async Pipe emits
  • A Signal read by the template changes
  • markForCheck() is called
  • detectChanges() is called

OnPush Strategy Flow

flowchart TD

InputChanged

EventOccurred

SignalUpdated

AsyncPipe

ManualCheck

InputChanged --> Component

EventOccurred --> Component

SignalUpdated --> Component

AsyncPipe --> Component

ManualCheck --> Component

Component --> DOM

Architecture Comparison

flowchart LR

subgraph Default

Trigger1[Trigger]

Trigger1 --> Root1[Root]

Root1 --> AllComponents[All Components Checked]

end

subgraph OnPush

Trigger2[Trigger]

Trigger2 --> Eligible[Only Eligible Components]

Eligible --> DOM

end

Default vs OnPush

Feature Default OnPush
Component Checked Every CD Cycle ✅ Yes ❌ No
Better Performance
Simpler to Use ⚠️
Enterprise Ready ⚠️
Large Applications ⚠️
CPU Usage Higher Lower
Rendering Efficiency Moderate High

How OnPush Detects Changes

Angular does not continuously monitor every property.

Instead, it reacts to specific triggers.

Example

@Input()

user!: User;

Parent

this.user = {

...this.user,

name:'John'

};

A new object reference causes Angular to check the OnPush component.


Object Reference Example

flowchart LR

OldObject

OldObject --> NewObject

NewObject --> OnPushComponent

OnPushComponent --> UI

Signals with OnPush

Signals work naturally with OnPush.

count = signal(0);

increment(){

this.count.update(

value => value + 1

);

}

Template

<h2>{{ count() }}</h2>

When count() changes,

Angular automatically marks the component for update.

This eliminates much of the manual work previously associated with OnPush.


Signals Architecture

flowchart LR

Signal

Signal --> OnPushComponent

OnPushComponent --> DOM

Async Pipe with OnPush

users$ =

this.http

.get<User[]>(this.api);

Template

<div *ngFor="let user of users$ | async">

{{ user.name }}

</div>

Whenever the Observable emits,

Angular updates the OnPush component automatically.


ChangeDetectorRef

Angular provides manual control when needed.

constructor(

private cd:

ChangeDetectorRef

){}

Methods

Method Purpose
markForCheck() Schedule an OnPush component for checking
detectChanges() Immediately run Change Detection
detach() Stop automatic checks
reattach() Resume automatic checks

Example: markForCheck()

loadData(){

this.http

.get<User[]>(this.api)

.subscribe(users=>{

this.users = users;

this.cd.markForCheck();

});

}

Useful when data changes outside Angular's normal triggers.


Example: detach()

constructor(

private cd:

ChangeDetectorRef

){

this.cd.detach();

}

Later

refresh(){

this.cd.detectChanges();

}

Useful for:

  • Dashboards
  • Live charts
  • Large tables
  • High-frequency updates

Enterprise Banking Example

Online Banking Dashboard

Components

  • Customer Summary
  • Account Overview
  • Portfolio
  • Transactions
  • Investments
  • Notifications

Recommended Architecture

  • OnPush for feature components
  • Signals for UI state
  • RxJS for APIs
  • Async Pipe for Observables
  • Immutable updates
flowchart TD

API

API --> Observable

Observable --> Signal

Signal --> OnPushComponents

OnPushComponents --> Dashboard

Dashboard --> User

Benefits

  • Faster rendering
  • Reduced CPU usage
  • Better scalability
  • Smaller Change Detection scope
  • Improved responsiveness

Performance Comparison

Scenario Default OnPush
Small App ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
Enterprise Dashboard ⭐⭐⭐ ⭐⭐⭐⭐⭐
Large Data Grid ⭐⭐ ⭐⭐⭐⭐⭐
Financial Dashboard ⭐⭐⭐ ⭐⭐⭐⭐⭐
Memory Usage Higher Lower
Rendering Efficiency Moderate Excellent

Performance Best Practices

Practice Benefit
Prefer OnPush for feature components Better rendering performance
Use immutable updates Reliable change detection
Combine OnPush with Signals Fine-grained updates
Use Async Pipe Automatic subscription handling
Use trackBy with lists Fewer DOM updates
Split large components Smaller component trees
Keep templates lightweight Faster rendering
Measure before optimizing Avoid premature optimization

Common Mistakes

Mutating Objects

❌ Wrong

this.user.name = 'John';

No new reference is created.


✅ Correct

this.user = {

...this.user,

name:'John'

};

Forgetting Async Pipe

Instead of manual subscriptions,

prefer

{{ users$ | async }}

when rendering Observable data.


Calling detectChanges() Excessively

Avoid unnecessary manual Change Detection.

Use Angular's automatic mechanisms whenever possible.


Using Default Strategy Everywhere

Default is simple, but enterprise applications often benefit from OnPush.

Choose the strategy based on the application's needs.


Heavy Template Logic

❌ Avoid

{{ calculateTotal() }}

Prefer

total = computed(() =>

this.calculateTotal()

);

Default vs OnPush Decision Guide

Application Type Recommended Strategy
Learning Project Default
Small CRUD App Default
Admin Dashboard OnPush
Banking System OnPush
E-Commerce Platform OnPush
Analytics Dashboard OnPush
Real-Time Monitoring OnPush + Signals
Enterprise Applications OnPush + Signals + RxJS

Best Practices

  • Start with the simplest strategy that meets your needs.
  • Prefer OnPush for reusable feature components.
  • Use immutable state updates.
  • Combine OnPush with Signals.
  • Continue using RxJS for asynchronous operations.
  • Use Async Pipe with Observables.
  • Keep templates simple.
  • Use trackBy for large lists.
  • Avoid unnecessary manual Change Detection.
  • Profile performance before making optimizations.

Advantages

Strategy Advantages
Default Simplicity, less setup, ideal for beginners
OnPush Better performance, fewer checks, enterprise scalability
Signals + OnPush Fine-grained rendering, cleaner reactive code

Top 10 Default vs OnPush Interview Questions

1. What is the difference between Default and OnPush Change Detection?

Answer

Default checks components during every Change Detection cycle, while OnPush checks components only when specific triggers occur, such as new input references, template events, Signal updates, Async Pipe emissions, or manual Change Detection.


2. When should you use OnPush?

Answer

Use OnPush for medium to large applications, reusable feature components, dashboards, data-intensive screens, and enterprise systems where rendering performance is important.


3. What triggers an OnPush component to update?

Answer

  • New Input reference
  • Component or child event
  • Async Pipe emission
  • Signal update
  • markForCheck()
  • detectChanges()

4. Why doesn't object mutation work well with OnPush?

Answer

OnPush relies primarily on reference changes for inputs. Mutating an existing object keeps the same reference, so Angular cannot detect that change through input reference comparison.


5. How do Signals work with OnPush?

Answer

When a template reads a Signal, Angular tracks that dependency. Updating the Signal automatically marks the component for rendering without requiring manual subscriptions.


6. What is markForCheck()?

Answer

It marks an OnPush component so Angular includes it during the next Change Detection cycle.


7. What is detectChanges()?

Answer

It immediately runs Change Detection for the current component and its child components.


8. Is OnPush always faster than Default?

Answer

Not always. Small applications may not see noticeable benefits. OnPush provides the greatest value in larger or more complex applications with many components.


9. What are common OnPush best practices?

Answer

Use immutable updates, Signals for UI state, Async Pipe for Observables, trackBy in lists, and avoid unnecessary manual Change Detection.


Answer

Use OnPush for feature components, Signals for synchronous UI state, RxJS for asynchronous workflows, Async Pipe for Observable rendering, and immutable state updates.


Interview Cheat Sheet

Requirement Recommended Solution
Simple Application Default
Enterprise Application OnPush
UI State Signals
Async Operations RxJS
Observable Rendering Async Pipe
Manual Refresh detectChanges()
Schedule Next Check markForCheck()
Large Lists trackBy
Better Performance OnPush + Signals
Immutable State Recommended

Summary

Angular offers two Change Detection strategies: Default and OnPush. The Default strategy prioritizes simplicity by checking components during every Change Detection cycle, while OnPush improves scalability by checking components only when meaningful triggers occur. Modern Angular applications achieve excellent performance by combining OnPush, Signals, RxJS, Async Pipe, and immutable state updates. Choosing the appropriate strategy depends on application size, complexity, and performance requirements.


Key Takeaways

  • ✔ Default Change Detection is simple and beginner-friendly.
  • ✔ OnPush reduces unnecessary component checks.
  • ✔ OnPush reacts to input reference changes, events, Async Pipe emissions, Signal updates, and manual triggers.
  • ✔ Signals integrate seamlessly with OnPush.
  • ✔ Use immutable updates for reliable OnPush behavior.
  • ✔ Async Pipe works naturally with OnPush.
  • ChangeDetectorRef provides manual control when needed.
  • ✔ Use trackBy for better list rendering performance.
  • ✔ OnPush is recommended for most enterprise feature components.
  • ✔ Understanding Default vs OnPush is a core Angular interview topic.

Next Article

➡️ 90-Zone.js-QA.md