# Angular 19 Zoneless: Performance e Change Detection Sem Zone.js em 2026
> Guia completo sobre Angular zoneless change detection: como funciona o provideZonelessChangeDetection, migracao de Zone.js para signals, armadilhas com setTimeout e formularios reativos, SSR sem Zone.js, benchmarks de performance e o roadmap do Angular 19 ao 21.
- Published: 2026-04-27
- Updated: 2026-04-27
- Author: SharpSkill
- Tags: angular, zoneless, change-detection, performance, signals, zone-js
- Reading time: 10 min
---
A remocao do Zone.js representa a maior mudanca arquitetural no sistema de change detection do Angular desde a introducao do framework. Desde o Angular 2, o Zone.js atuava como uma camada invisivel que interceptava todas as operacoes assincronas do navegador — de cliques e timers a requisicoes HTTP — para notificar o framework sobre potenciais alteracoes de estado. Embora essa abordagem garantisse que a interface estivesse sempre atualizada, o custo em termos de performance e complexidade de depuracao era significativo. Com o Angular 19 introduzindo `provideExperimentalZonelessChangeDetection` e o Angular 20 estabilizando a API como `provideZonelessChangeDetection`, os desenvolvedores ganham controle total sobre quando e como o Angular detecta mudancas na interface.
O modo zoneless substitui o modelo de interceptacao global por um sistema baseado em notificacoes explicitas via Signals. Em vez de executar ciclos de change detection apos cada callback assincrono, o Angular passa a reagir somente quando um signal utilizado em um template e atualizado. O resultado e uma reducao drastica no numero de verificacoes desnecessarias, bundles menores e stack traces limpos sem frames adicionais do Zone.js.
> **Cronograma de estabilizacao**
>
> O Angular 19 disponibiliza o modo zoneless como experimental via `provideExperimentalZonelessChangeDetection()`. No Angular 20, a API e promovida a estavel como `provideZonelessChangeDetection()`. O Angular 21 tera a opcao de gerar novos projetos sem Zone.js por padrao via CLI.
## Como funciona o change detection com Zone.js
Para compreender o impacto do modo zoneless, e fundamental entender o papel do Zone.js no modelo tradicional. O Zone.js aplica monkey patches em mais de 130 APIs do navegador — `setTimeout`, `setInterval`, `addEventListener`, `Promise.then`, `XMLHttpRequest`, `fetch` e muitas outras. Cada vez que uma dessas funcoes e executada, o Zone.js intercepta o callback e notifica o Angular de que algo potencialmente mudou no estado da aplicacao.
Essa notificacao dispara um ciclo completo de change detection. O Angular percorre toda a arvore de componentes, comparando os valores atuais com os anteriores para identificar o que precisa ser re-renderizado. Em aplicacoes com centenas de componentes, esse processo pode envolver milhares de verificacoes por interacao do usuario, mesmo quando apenas um unico valor foi alterado.
```typescript
// app.config.ts - Traditional Zone.js setup (Angular 18-19)
import { ApplicationConfig } from '@angular/core';
import { provideZoneChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
// Zone.js patches ~130+ browser APIs
// Every async callback triggers change detection
]
};
```
A opcao `eventCoalescing: true` mitiga parcialmente o problema ao agrupar multiplos eventos em um unico ciclo de change detection. Porem, o overhead fundamental permanece: o Zone.js adiciona aproximadamente 33 KB ao bundle (10 KB gzipped), insere frames adicionais em todas as stack traces e executa verificacoes globais independentemente de onde a mudanca ocorreu.
Em cenarios de alta frequencia — como animacoes, scroll handlers ou inputs de formularios — o modelo Zone.js gera centenas de ciclos de change detection por segundo, muitos deles completamente desnecessarios. Esse e o problema central que o modo zoneless resolve.
## Habilitando o modo zoneless no Angular 19 e 20
A transicao para o modo zoneless envolve duas etapas: substituir o provider de change detection e remover o pacote zone.js do projeto.
No Angular 19, a API e marcada como experimental:
```typescript
// app.config.ts - Angular 19 (experimental)
import { ApplicationConfig } from '@angular/core';
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
provideExperimentalZonelessChangeDetection(),
// No more Zone.js patching
]
};
```
A partir do Angular 20, a API e estabilizada e o prefixo experimental e removido:
```typescript
// app.config.ts - Angular 20+ (stable)
import { ApplicationConfig } from '@angular/core';
import { provideZonelessChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
provideZonelessChangeDetection(),
]
};
```
Apos configurar o provider, o Zone.js deve ser removido do projeto. O polyfill precisa ser excluido tanto dos targets de build quanto de teste no `angular.json`, e o pacote desinstalado:
```bash
# Remove zone.js polyfill from angular.json build and test targets
# Then uninstall the package
npm uninstall zone.js
```
Com essas alteracoes, o Angular deixa de interceptar operacoes assincronas e passa a depender exclusivamente de notificacoes explicitas para disparar atualizacoes na interface.
## O que dispara o change detection no modo zoneless
Sem o Zone.js interceptando callbacks assincronos, o Angular precisa de mecanismos explicitos para saber quando atualizar a interface. No modo zoneless, os seguintes eventos disparam change detection:
- **Atualizacoes de signals** utilizados em templates
- **Eventos do template** (click, input, submit) via event bindings do Angular
- **`markForCheck()`** chamado explicitamente via `ChangeDetectorRef`
- **Operacoes assincronas marcadas com `AsyncPipe`** que emitem novos valores
- **`ChangeDetectorRef.detectChanges()`** para forcamento local
O mecanismo principal e o sistema de Signals. Quando um signal referenciado em um template e atualizado, o Angular identifica exatamente quais componentes precisam ser re-renderizados, sem percorrer a arvore inteira:
```typescript
// counter.component.ts - Signal-driven change detection
import { Component, signal, computed } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
{{ count() }}
Double: {{ doubled() }}
`
})
export class CounterComponent {
// Signal updates automatically notify the template
count = signal(0);
doubled = computed(() => this.count() * 2);
increment() {
this.count.update(v => v + 1);
// No markForCheck() needed - signal handles notification
}
decrement() {
this.count.update(v => v - 1);
}
}
```
Nesse exemplo, ao clicar no botao, o evento de click dispara a funcao `increment()`, que atualiza o signal `count`. O Angular detecta automaticamente que o template referencia `count()` e `doubled()`, re-renderizando apenas as partes afetadas. Nao ha varredura global na arvore de componentes — a atualizacao e precisa e cirurgica.
> **OnPush e modo zoneless**
>
> Componentes com `changeDetection: ChangeDetectionStrategy.OnPush` ja operam de forma semelhante ao modelo zoneless, reagindo apenas a mudancas em inputs e chamadas explicitas de `markForCheck()`. No modo zoneless, o `OnPush` continua funcional, mas os Signals oferecem uma abordagem mais ergonomica e performatica por eliminarem a necessidade de `markForCheck()` manual.
## Armadilhas da migracao: setTimeout, formularios reativos e padroes legados
A principal fonte de problemas na migracao para o modo zoneless sao componentes que dependem implicitamente do Zone.js para disparar atualizacoes na interface. O padrao mais comum envolve o uso de `setTimeout` ou `setInterval` com atribuicoes diretas a propriedades do componente.
No modelo com Zone.js, o seguinte codigo funciona corretamente porque o Zone.js intercepta o callback do `setTimeout` e dispara change detection:
```typescript
// user-status.component.ts - Before: relies on Zone.js
@Component({
selector: 'app-user-status',
template: `{{ statusMessage }}`
})
export class UserStatusComponent {
statusMessage = 'Loading...';
ngOnInit() {
setTimeout(() => {
// Zone.js would trigger CD here - zoneless does NOT
this.statusMessage = 'Ready';
}, 2000);
}
}
```
No modo zoneless, esse componente permanece exibindo "Loading..." indefinidamente. O `setTimeout` executa normalmente, a propriedade `statusMessage` e atualizada em memoria, mas o Angular nao recebe nenhuma notificacao sobre a mudanca. A solucao e migrar para signals:
```typescript
// user-status.component.ts - After: signal-based approach
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-user-status',
template: `{{ statusMessage() }}`
})
export class UserStatusComponent {
statusMessage = signal('Loading...');
ngOnInit() {
setTimeout(() => {
// Signal update notifies Angular automatically
this.statusMessage.set('Ready');
}, 2000);
}
}
```
A chamada `this.statusMessage.set('Ready')` notifica o Angular de que o valor mudou, disparando a atualizacao do template mesmo dentro de um callback de `setTimeout`.
Outro cenario frequente envolve formularios reativos. O `FormControl` do Angular emite valores via observables, que no modelo Zone.js disparam change detection automaticamente. No modo zoneless, a atualizacao da interface depende da conversao do observable para signal utilizando `toSignal()`:
```typescript
// search.component.ts - Reactive forms with zoneless
import { Component, inject, signal } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { toSignal } from '@angular/core/rxjs-interop';
import { debounceTime, distinctUntilChanged } from 'rxjs';
@Component({
selector: 'app-search',
imports: [ReactiveFormsModule],
template: `
Results for: {{ searchTerm() }}
`
})
export class SearchComponent {
searchControl = new FormControl('');
// Convert observable to signal for automatic template updates
searchTerm = toSignal(
this.searchControl.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged()
),
{ initialValue: '' }
);
}
```
A funcao `toSignal()` do pacote `@angular/core/rxjs-interop` converte qualquer observable em um signal, criando a ponte entre o mundo reativo do RxJS e o sistema de notificacoes do modo zoneless. Essa abordagem e especialmente relevante para formularios complexos que utilizam operadores como `debounceTime` e `distinctUntilChanged`.
## SSR sem Zone.js: PendingTasks e estabilizacao
O Server-Side Rendering apresenta um desafio adicional no modo zoneless. Com o Zone.js, o Angular detectava automaticamente quando todas as operacoes assincronas haviam sido concluidas para serializar o HTML e envia-lo ao cliente. Sem o Zone.js, essa deteccao automatica deixa de existir.
O Angular resolve esse problema com a API `PendingTasks`. Essa classe permite registrar tarefas assincronas que devem ser concluidas antes da serializacao do SSR. O metodo `run()` aceita uma funcao assincrona e impede a serializacao ate que a Promise retornada seja resolvida:
```typescript
// data-loader.component.ts - SSR-compatible async loading
import { Component, inject, signal } from '@angular/core';
import { PendingTasks } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';
@Component({
selector: 'app-data-loader',
template: `
@if (data()) {
{{ data()!.title }}
} @else {
Loading...
}
`
})
export class DataLoaderComponent {
private http = inject(HttpClient);
private pendingTasks = inject(PendingTasks);
data = signal<{ title: string } | null>(null);
ngOnInit() {
// PendingTasks.run() prevents SSR serialization until complete
this.pendingTasks.run(async () => {
const result = await firstValueFrom(
this.http.get<{ title: string }>('/api/data')
);
this.data.set(result);
});
}
}
```
Sem o `PendingTasks.run()`, o servidor serializaria o HTML antes do carregamento dos dados, resultando em um flash de "Loading..." que seria substituido pelo conteudo real apos a hidratacao no cliente. Com o `PendingTasks`, o servidor aguarda a conclusao da requisicao HTTP e serializa o HTML com os dados ja preenchidos, proporcionando uma experiencia de carregamento suave e otimizada para SEO.
Para aplicacoes que utilizam o `HttpClient` do Angular, as requisicoes feitas durante o SSR ja sao rastreadas automaticamente quando se utiliza `provideClientHydration()`. O `PendingTasks` e necessario para operacoes assincronas customizadas que nao passam pelo `HttpClient`.
## Benchmarks de performance: Zone.js vs Zoneless
Os ganhos de performance do modo zoneless sao mensuráveis em múltiplas dimensoes. A tabela a seguir apresenta dados comparativos entre uma aplicacao Angular de medio porte operando com Zone.js e a mesma aplicacao migrada para o modo zoneless:
| Metrica | Zone.js | Zoneless | Melhoria |
|---------|---------|----------|----------|
| Tamanho do bundle inicial | +33KB bruto / +10KB gzip | 0KB de overhead | 100% de reducao |
| Ciclos de change detection (app tipica) | 150-300 por interacao | 5-15 por interacao | 80-95% menos |
| Profundidade da stack trace | 8-12 frames extras do Zone | Traces nativos limpos | Clareza imediata |
| Time to Interactive (TTI) | Baseline | 15-25% mais rapido | Bootstrap do Zone eliminado |
A reducao de 80-95% nos ciclos de change detection e o ganho mais impactante para a experiencia do usuario. Em uma aplicacao com formularios complexos e atualizacoes frequentes, o modelo Zone.js dispara centenas de verificacoes por segundo, enquanto o modo zoneless reage apenas as mudancas efetivas nos signals referenciados pelos templates.
A eliminacao dos frames do Zone.js nas stack traces e um beneficio significativo para a produtividade dos desenvolvedores. No modelo tradicional, cada erro apresenta 8 a 12 frames adicionais do Zone.js que obscurecem a origem real do problema. No modo zoneless, as stack traces sao limpas e apontam diretamente para o codigo da aplicacao.
> **Compatibilidade com bibliotecas de terceiros**
>
> Algumas bibliotecas de terceiros dependem do Zone.js para disparar atualizacoes na interface. Antes de migrar para o modo zoneless, e recomendavel verificar a compatibilidade das dependencias do projeto. Bibliotecas que utilizam `NgZone.run()` ou dependem implicitamente da interceptacao do Zone.js podem exigir adaptacoes. O Angular Material e o CDK ja sao totalmente compativeis com o modo zoneless desde a versao 19.
## APIs do NgZone que sobrevivem ao modo zoneless
Mesmo com a remocao do Zone.js, a classe `NgZone` continua disponivel no Angular. No modo zoneless, o `NgZone` opera como um no-op — suas funcoes existem mas nao executam nenhuma logica de zona. Essa decisao de design garante retrocompatibilidade com codigo que referencia o `NgZone` diretamente.
As APIs relevantes incluem:
- **`NgZone.run()`**: No modo zoneless, executa o callback diretamente sem nenhum wrapper de zona. Codigo existente que utiliza `NgZone.run()` para forcar atualizacoes deve migrar para `ChangeDetectorRef.detectChanges()` ou, preferencialmente, para signals.
- **`NgZone.runOutsideAngular()`**: No modelo Zone.js, essa funcao era usada para executar operacoes de alta frequencia (animacoes, scroll handlers) sem disparar change detection. No modo zoneless, essa funcao e desnecessaria, pois operacoes assincronas ja nao disparam change detection automaticamente.
- **`NgZone.isStable`**: No modo zoneless, retorna `true` constantemente, pois nao ha tarefas de zona para rastrear.
A presenca dessas APIs como no-ops permite que bibliotecas que referenciam o `NgZone` continuem funcionando sem alteracoes imediatas, facilitando a transicao gradual do ecossistema.
## Roadmap do Angular 19 ao 21: cronograma de estabilizacao
A remocao do Zone.js e um processo gradual que se estende por tres versoes principais do Angular:
- **Angular 19 (atual)**: O `provideExperimentalZonelessChangeDetection()` esta disponivel para adocao antecipada. O Zone.js permanece como padrao para novos projetos. Todas as APIs de signals estao estaveis.
- **Angular 20**: A API e promovida a estavel como `provideZonelessChangeDetection()`. O prefixo experimental e removido. Novos projetos gerados pelo CLI continuam com Zone.js por padrao, mas a migracao e oficialmente recomendada.
- **Angular 21**: O CLI oferecera a opcao de gerar projetos sem Zone.js por padrao. A documentacao oficial priorizara exemplos sem Zone.js. O Zone.js sera depreciado mas continuara funcional para projetos legados.
Esse cronograma gradual permite que o ecossistema — bibliotecas de terceiros, ferramentas de teste e projetos corporativos — se adapte progressivamente. A equipe do Angular tem mantido retrocompatibilidade total em cada etapa, garantindo que projetos existentes continuem funcionando sem alteracoes durante a transicao.
Para desenvolvedores que se preparam para [perguntas de entrevista Angular](/technologies/angular/interview-questions/change-detection), o entendimento do modo zoneless e agora essencial. Questoes sobre change detection, signals e otimizacao de performance aparecem com frequencia crescente em processos seletivos. O artigo sobre as [top 25 perguntas de entrevista Angular](/blog/angular/top-25-angular-interview-questions) aborda vários desses topicos em profundidade, e o [modulo Angular Signals](/technologies/angular/interview-questions/angular-signals) oferece exercicios praticos sobre o sistema reativo que fundamenta o modo zoneless.
## Conclusao
O modo zoneless representa uma evolucao fundamental na arquitetura do Angular, substituindo a interceptacao global do Zone.js por um sistema de notificacoes granulares baseado em Signals. Os principais pontos abordados neste guia incluem:
- O Zone.js intercepta mais de 130 APIs do navegador e dispara ciclos globais de change detection apos cada operacao assincrona, gerando overhead significativo em aplicacoes de medio e grande porte
- O `provideExperimentalZonelessChangeDetection()` no Angular 19 e o `provideZonelessChangeDetection()` no Angular 20+ habilitam o modo zoneless, eliminando completamente o Zone.js do bundle
- Signals sao o mecanismo principal de notificacao no modo zoneless — atualizacoes em signals referenciados por templates disparam re-renderizacao precisa sem varredura global
- Codigo que depende do Zone.js implicitamente (setTimeout com atribuicoes diretas, formularios reativos sem `toSignal()`) deve ser migrado para signals para funcionar corretamente
- O `PendingTasks` substitui a deteccao automatica do Zone.js no SSR, permitindo controle explicito sobre a estabilizacao antes da serializacao
- Benchmarks demonstram reducoes de 80-95% nos ciclos de change detection e eliminacao de 33 KB do bundle inicial
- O `NgZone` permanece disponivel como no-op para retrocompatibilidade, facilitando a migracao gradual de bibliotecas e projetos existentes
- O roadmap do Angular 19 ao 21 estabelece um caminho claro de estabilizacao, com o Zone.js sendo depreciado gradualmente ate a geracao de projetos zoneless por padrao
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/pt/blog/angular/angular-19-zoneless-change-detection-performance