---
title: "Zoneless Change Detection in Angular 18: Boost Performance"
published_at: "2024-11-25T11:29:20+00:00"
modified_at: "2026-02-10T11:24:36+00:00"
url: "https://www.syncfusion.com/blogs/post/zoneless-change-detection-angular-18"
excerpt: "Discover how zoneless change detection in Angular 18 eliminates the need for Zone.js, improving performance and simplifying change detection."
taxonomy_category:
  - "Angular"
  - "Front-End Development"
  - "JavaScript Frameworks"
  - "Performance Optimization"
  - "Web Development"
taxonomy_post_tag:
  - "Angular18"
  - "productivity"
  - "WebPerformance"
  - "What's New"
  - "ZoneJS"
---

# Zoneless Change Detection in Angular 18: Boost Performance

[Ankit Sharma](https://www.syncfusion.com/blogs/author/ankit-sharma)

![Zoneless Change Detection in Angular 18: Boost Performance](https://www.syncfusion.com/blogs/wp-content/uploads/2024/11/Zoneless-Change-Detection-in-Angular-18-Boost-Performance.jpg)


**TL;DR:** Angular 18 introduced zoneless change detection, an experimental feature to replace Zone.js. It improves performance by manually controlling when change detection occurs, reducing unnecessary cycles, and enhancing app efficiency.

Change detection is the process by which Angular ensures that the user interface (UI) reflects the current state of the application. Traditionally, Angular used [Zone.js](https://www.npmjs.com/package/zone.js)
 to track asynchronous operations and trigger change detection when necessary.

[Angular 18](https://blog.angular.dev/angular-v18-is-now-available-e79d5ac0affe)
 has introduced an exciting experimental feature: zoneless change detection. This innovation promises to enhance performance by eliminating the need for **Zone.js**, which has been a cornerstone of Angular’s change detection mechanism. In this blog, we’ll look at the concepts, benefits, and implementation of zoneless change detection.

[Syncfusion Angular components are:LightweightModularHigh-performingExplore Now](https://www.syncfusion.com/angular-components)
## What is change detection in Angular?

In Angular, change detection refers to the process through which the framework monitors an app’s state and updates the view accordingly. Angular uses **Zone.js** to track asynchronous tasks such as HTTP requests or timers, and it triggers change detection based on these operations. Zone.js “monkey-patches” browser APIs like ** setTimeout** and ** addEventListener**, allowing Angular to recognize when asynchronous events happen.

Angular organizes components in a tree structure, with the root being the **AppComponent**, and all other components branching out as children. Each component has its own change detector. When a change occurs, Angular checks each component in a bottom-up fashion, updating only the necessary parts of the view.

When an event like a user interaction or an HTTP response occurs, Angular starts a tick cycle. It begins at the root of the change detection tree and compares the current data of each component with its previous state. If a difference is found, the view is updated. Otherwise, Angular moves on to the next component without making changes.

Angular provides the following two change detection strategies:

- **Default strategy:** Angular checks all components in the tree when any change happens.
- **OnPush strategy**: Angular checks the component only if its inputs have changed, or if an event is emitted inside the component. This improves performance, especially in large apps, by limiting unnecessary checks.

## How does change detection work in Angular?

Let’s create an Angular app and see zone-based change detection in action.

Run the following command to create an Angular app.

```
ng new zoneless-app
```

Once the project is created, open the **src\app\app.component.ts** file and put the following code into it.

```
import { Component, inject, OnInit, OnDestroy } from '@angular/core'; 
import { RouterOutlet } from '@angular/router'; 
import { AsyncPipe } from '@angular/common'; 
import { map, catchError, of } from 'rxjs'; 
import { HttpClient } from '@angular/common/http'; 

@Component({ 
  selector: 'app-root', 
  standalone: true, 
  imports: [RouterOutlet, AsyncPipe], 
  templateUrl: './app.component.html', 
  styleUrls: ['./app.component.css'], 
}) 
export class AppComponent implements OnInit, OnDestroy { 

  private readonly httpClient = inject(HttpClient); 
  private intervalId: any; // For managing the setInterval lifecycle.

  currentCount = 0; 
  timer = 0; 
  movies$ = this.fetchMovieData(); 

  incrementCount() { 
    this.currentCount++; 
  } 

  ngOnInit() { 
    this.intervalId = setInterval(() => { 
      this.timer++; 
    }, 1000); 
  }

  ngOnDestroy() {
    if (this.intervalId) {
      clearInterval(this.intervalId); // Clear the interval on destroy.
    }
  } 

  fetchMovieData() { 
    return this.httpClient 
      .get<any[]>('https://dummyapi.online/api/movies') 
      .pipe(
        map((movieList) => movieList.slice(0, 5)),
        catchError((error) => {
          console.error('Error fetching movies', error);
          return of([]); // return empty array in case of error.
        })
      ); 
  } 
}
```

The **incrementCount** method increases the ** currentCount** property by 1. In the ** ngOnInit** lifecycle hook, we use the ** setInterval** function to increment the ** timer** property every second. The ** fetchMovieData** method makes an ** HTTP GET** request to retrieve movie data from a publicly available API. It utilizes the ** map** operator to transform the response, slicing the movie list to return only the first five movies. The result is observable.

Open the **src\app\app.component.html** file and paste the following code into it.

```
<h3>Click Event</h3> 
<p>Current count: {{ currentCount }}</p> 
<button class="btn btn-primary" (click)="incrementCount()">Click me</button> 
 
<h3>Async timer</h3> 
<p>Timer value: {{ timer }}</p> 
 
<h3>Fetch data from HTTP call</h3> 
@for (movie of movies$ | async; track movie.id) { 
<p>{{ movie.movie }}</p> 
} 
```

We have a button that, when clicked, triggers the **incrementCount** method in the component class to increase the ** currentCount**. Additionally, the current value of the ** timer** property, which updates every second, will be displayed. We also use the ** async** pipe on the ** movies$** observable and loop through the data to display the movie names.

To run the app, use the command **ng serve**. Once the app is running, you’ll see the output as shown in the following.

![Zone.js demo app with live click counter, timer, and movie list](https://www.syncfusion.com/blogs/wp-content/uploads/2024/11/Run-the-app.gif)

Whenever the user interacts with the app, Zone.js informs Angular, triggering change detection to update the view. However, this approach comes with a performance cost. Zone.js causes change detection to run after every asynchronous operation, even if no meaningful changes have been made to the app state. This can lead to unnecessary computations, particularly in large apps.

To check if Zone.js is present, open the browser’s console and type **Zone** (with an uppercase ** Z**). You will see a description of the ** Zone** class, as demonstrated in the following image.

![Checking for Zone.js presence in console tab](https://www.syncfusion.com/blogs/wp-content/uploads/2024/11/Zone-browser-console.png) [https://ej2.syncfusion.com/angular/documentation/introduction/%20](https://ej2.syncfusion.com/angular/documentation/introduction/%20)

## What is zoneless change detection?

Zoneless change detection in Angular 18 introduces a more manual and precise way to manage change detection. This approach gives developers finer control over when change detection is triggered, improving performance and reducing memory overhead. By eliminating the reliance on **Zone.js**, Angular avoids unnecessary change detection cycles, resulting in faster and more efficient apps.

## Advantages of not using Zone.js

Removing **Zone.js** as a dependency has several key benefits:

### Better performance

Zone.js listens to DOM events and async tasks to trigger change detection, even when no real state change happens. This leads to unnecessary updates, slowing down the app.

### Improved core web vitals

Zone.js adds extra load to the app, increasing both the file size and startup time.

### Easier debugging

Zone.js makes debugging harder, with more complicated stack traces and issues when code runs outside Angular’s zone.

### Better compatibility

Zone.js patches browser APIs, but it doesn’t cover all new APIs. Some, like async/await, can’t be patched well. Also, some libraries may not work smoothly with Zone.js. Removing it simplifies things and improves long-term compatibility.


## How to enable zoneless change detection in an Angular 18 app?

Angular 18 introduced an experimental API, **provideExperimentalZonelessChangeDetection**, which allows us to configure an app to avoid using the state or state changes of Zone.js for scheduling change detection. This enables a more streamlined approach to change detection, giving developers greater control over when it is triggered.

Update the **src\app\app.config.ts** file to include this next function.

```
import { provideExperimentalZonelessChangeDetection } from '@angular/core'; 
// Other existing imports. 
 
export const appConfig: ApplicationConfig = { 
  providers: [ 
    provideExperimentalZonelessChangeDetection(), 
// Existing providers. 
  ], 
}; 
```

We have removed the **provideZoneChangeDetection** function and included the new ** provideExperimentalZonelessChangeDetection** function.

Zone.js is usually included through the **polyfills** option in the ** angular.json** file for both build and test targets. To exclude it from the build, remove ** zone.js** and ** zone.js/testing** from both sections.

```
"build": { 
  "builder": "@angular-devkit/build-angular:application", 
  "options": { 
    ... 
    "polyfills": [ 
      "zone.js"     // Remove this line.  
     ], 
    "tsConfig": "tsconfig.app.json", 
    "scripts": [] 
  }, 
  ... 
}, 
 
"test": { 
  "builder": "@angular-devkit/build-angular:karma", 
  "options": { 
    "polyfills": [ 
      "zone.js",    // Remove this line. 
      "zone.js/testing" // Remove this line. 
    ], 
    "tsConfig": "tsconfig.spec.json", 
    "scripts": [] 
    ... 
  } 
} 
```

Finally, remove the Zone.js package from the app by running the following command.

```
npm uninstall zone.js
```

Now run the app again. Open the browser console and check for the presence of **Zone**. You will see the following error.

![Zone undefined error in browser console after removal](https://www.syncfusion.com/blogs/wp-content/uploads/2024/11/Zone-error-in-console.png)

This indicates that **Zone.js** is not available and our app is running in zoneless mode.

Check out the functionality of the app.  
 [https://www.syncfusion.com/blogs/wp-content/uploads/2024/11/App-running-in-zoneless-mode-with-click-events-and-async-data-updates-functioning-correctly.gif](https://www.syncfusion.com/blogs/wp-content/uploads/2024/11/App-running-in-zoneless-mode-with-click-events-and-async-data-updates-functioning-correctly.gif)
  
 You can see that the click event handler is functioning as expected, as each click triggers a change detection when the button is pressed. Similarly, data retrieval via the async pipe is working correctly, since it triggers change detection whenever a new value is emitted by the observable.

However, the **timer** property, which relies on ** setInterval**, is not being updated. In Zone.js-based change detection, events like ** setInterval** or ** setTimeout** are automatically detected. But with the zoneless change detection mechanism, changes within the ** setInterval** function do not automatically trigger updates. To reflect these changes in the view, we must explicitly inform Angular that a change has occurred.

There are two ways to notify Angular for change detection in this case:

- Using **ChangeDetectorRef.markForCheck()**
- Using **signals**

Let’s understand the implementation of both these techniques with the help of code examples.

### Using ChangeDetectorRef

Update the **AppComponent** class inside the ** src\app\app.component.ts** file as follows:

```
export class AppComponent implements OnInit { 
  // Existing code.
 
  private readonly changeDetectorRef = inject(ChangeDetectorRef); 
 
  ngOnInit() { 
    setInterval(() => { 
      this.timer++; 
      this.changeDetectorRef.markForCheck();  // Add this line to trigger change detection. 
    }, 1000); 
  } 
 
  // Existing code. 
} 
```

We have invoked the **markForCheck** method on the ** ChangeDetectorRef** instance within the ** setInterval** function. This instructs Angular to check the component for any changes.

Run the app again, and you’ll notice that the value of the **timer** property is now updating correctly. Refer to the following image.


### Using signals

Update the **AppComponent** class inside the ** src\app\app.component.ts** file.

```
export class AppComponent implements OnInit { 
  // Existing code. 
  timer = signal(0); 
 
  ngOnInit() { 
    setInterval(() => { 
      this.timer.update((t) => t + 1); 
    }, 1000); 
  } 
 
  // Existing code. 
}
```

We have defined the **timer** property as a signal, initializing it to zero. Inside the ** setInterval** function, we will use the signal’s update function to increment the value of the timer.

In the template, we will access the value of the signal using parentheses.

```
<h3>Async timer</h3> 
<p>Timer value: {{ timer() }}</p> 
```

Run the app again, and you’ll notice that the value of the **timer** property is now updating correctly.


## Conclusion

In this article, we explored how [Zone.js](https://www.npmjs.com/package/zone.js)
-based change detection works in Angular and its limitations. We also discussed the experimental zoneless change detection introduced in [Angular 18](https://blog.angular.dev/angular-v18-is-now-available-e79d5ac0affe)
 and how to activate it.

Zoneless Angular empowers developers to enhance change detection without the burden of automatic zone-based triggers. By effectively applying zoneless change detection in Angular, you can achieve notable performance gains and improve your app’s responsiveness.

## Related Blogs



[Master Angular Signals: Build Faster, Smarter Angular Apps](https://www.syncfusion.com/blogs/post/angular-signals-reactive-state)



[Angular Rich Text Editor 2025 Volume 3: Latest Features & Enhancements](https://www.syncfusion.com/blogs/post/angular-rich-text-editor-2025-vol-3)



[Essential Studio 2025 Volume 3: Latest UI Updates for React, Angular & JavaScript](https://www.syncfusion.com/blogs/post/javascript-ui-components-2025-vol-3)



[What’s New in Angular 19?](https://www.syncfusion.com/blogs/post/whats-new-in-angular-19)
