Modern Angular
NG0500 Hydration Node Mismatch: o conserto de verdade não é ngSkipHydration
NG0500: Hydration Node Mismatch quer dizer que o DOM que o Angular montou no cliente não bateu com o HTML que o seu servidor mandou. O conserto popular, o ngSkipHydration, silencia o erro jogando a hydration fora naquele trecho. A doc chama isso de last resort, e trata o componente que quebra como um bug. O NG0500 é um diagnóstico: algo alterou o DOM entre o servidor e o cliente, e quase sempre é um item de uma lista curta.
O erro, e a família
A mensagem abre com NG0500: During hydration Angular expected .... A descrição oficial é precisa: durante a hydration o Angular esperava a estrutura de DOM que ele renderizou e anotou no servidor, mas no cliente a árvore de DOM era diferente. A hydration reaproveita o DOM renderizado no servidor em vez de recriar, então qualquer divergência entre as duas árvores trava o processo.
O NG0500 é o título de uma família, e você pode ter colado um vizinho. NG0501 é siblings faltando, NG0502 um nó faltando, NG0503 projeção de nós de DOM não suportada, NG0504 uma flag de skip-hydration num nó inválido, NG0505 sem info de hydration na resposta do servidor, NG0506 uma aplicação que segue instável, e NG0507 HTML que foi alterado depois do server-side rendering. Todos levam à mesma ideia: o DOM do cliente não é o que o servidor prometeu.
O que de fato causa
As causas são uma lista curta, e a maioria não é sutil depois que você conhece. A sorrateira é HTML inválido. O browser conserta calado uma marcação malformada, então o servidor manda uma árvore e o browser interpreta outra, e a hydration vê um mismatch que você não causou de propósito.
As outras: código que mexe no DOM direto (APIs nativas, innerHTML, outerHTML, appendChild, mover nós), bibliotecas de terceiro que renderizam no DOM (a doc cita os gráficos do D3), e o preserveWhitespaces setado diferente entre o build do servidor e o do cliente.
| Causa | Por que a hydration quebra | Direção do fix |
|---|---|---|
| Aninhamento de HTML inválido | O browser normaliza; a árvore do cliente difere da do servidor | Conserte a marcação pra as duas darem a mesma árvore |
| DOM manipulado direto | innerHTML, appendChild, mover nós muda a árvore | Use binding do Angular ou Renderer2 |
| Biblioteca de DOM de terceiro (D3, etc.) | Ela muta o DOM durante e depois do SSR | Rode só no cliente, depois da hydration |
| preserveWhitespaces inconsistente | Os builds de servidor e cliente emitem whitespace diferente | Alinhe o setting nos dois builds |
<!-- Quebra a hydration: o browser tira o <div> de dentro do <p>,
então a árvore do cliente não bate mais com o HTML do servidor -->
<p>{{ summary }} <div class="badge">{{ status }}</div></p>
<!-- Válido: servidor e cliente interpretam a mesma árvore -->
<p>{{ summary }} <span class="badge">{{ status }}</span></p>ngSkipHydration é torniquete, não cura
Busque NG0500 e a resposta do topo é ngSkipHydration. Funciona, e é esse o problema. O atributo força o Angular a pular a hydration do componente inteiro e dos filhos, então o erro some porque a hydration some naquele trecho. Coloque na raiz e você desligou a hydration do app inteiro, mas seguiu pagando o custo do SSR.
A doc é direta sobre o papel dele: é um last resort, e um componente que quebra a hydration deve ser tratado como bug a consertar. Então, se você for usar, dê escopo no host mais estreito, e trate como o playbook de SSR e hydration trata: todo ngSkipHydration ganha um dono, um motivo e um plano de remoção, ou vira cicatriz permanente que ninguém tem coragem de mexer.
Os fixes de verdade, por causa
Aninhamento inválido: conserte a marcação pra o HTML do servidor e o DOM interpretado pelo browser serem a mesma árvore. Um <div> dentro de um <p>, um <a> dentro de um <a>, uma <table> sem <tbody>, esses são os suspeitos de sempre.
DOM manipulado direto: leve pro lado das APIs do Angular, binding, Renderer2, o template, pra o framework ser dono da estrutura nos dois lados. Quando você precisa mesmo de uma biblioteca de DOM que pinta depois que a página existe, rode só no cliente com afterNextRender, que dispara depois da hydration e nunca no servidor, então não sobra nada pra hydration estranhar.
// Quebra a hydration: a lib de gráfico muta o DOM no servidor e no
// cliente, e as duas árvores não batem
ngOnInit() { this.renderChart(this.host.nativeElement); }
// Resolvido: rode só no cliente, depois que a hydration termina
private readonly host = inject(ElementRef);
constructor() {
afterNextRender(() => this.renderChart(this.host.nativeElement));
}Quando só quebra em produção
Alguns NG0500 passam local e falham só depois do deploy. A regra que a doc declara é que o HTML produzido pelo SSR não pode ser alterado entre o servidor e o cliente. Qualquer coisa no meio que reescreve a resposta quebra isso: um proxy ou CDN que minifica HTML, um edge worker que injeta uma tag, uma camada de segurança que reordena atributos. O servidor renderizou uma árvore, o browser recebeu outra, e o NG0507 é o código explícito pra HTML alterado depois do rendering.
Então, quando o componente está limpo mas o erro é real, olhe o caminho entre o seu servidor e o browser antes de pegar o ngSkipHydration. O fix é parar a reescrita, não pular a hydration de um componente que nunca foi o problema.
Artefato para reaproveitar
Depurando o mismatch de hydration NG0500
- Leia qual nó a mensagem nomeia; é ali que a árvore do servidor e a do cliente divergiram.
- Olhe o template atrás de aninhamento de HTML inválido (div em p, a em a, table sem tbody); o browser reescreve.
- Ache o DOM manipulado direto (innerHTML, appendChild, APIs nativas) e leve pra binding do Angular ou Renderer2.
- Pra biblioteca de DOM de terceiro, rode só no cliente com afterNextRender pra ela nunca rodar no SSR.
- Se só falha em produção, cheque proxy ou CDN alterando o HTML no caminho (NG0507).
- Use ngSkipHydration só como last resort, com escopo no host mais estreito, com dono e plano de remoção.
Perguntas frequentes
O que quer dizer o NG0500 Hydration Node Mismatch?
Na hydration, o Angular reaproveita o DOM que o seu servidor renderizou. O NG0500 quer dizer que a árvore de DOM no cliente ficou diferente da que o servidor produziu, então o Angular não consegue casar as duas. Algo mudou a estrutura entre o servidor e o cliente.
É só adicionar o ngSkipHydration?
Só como last resort. A doc diz que ele força o Angular a pular a hydration do componente e dos filhos, então você mantém o custo do SSR mas perde a hydration naquele trecho, e chama de bug um componente que quebra a hydration. Se usar, dê escopo estreito e um plano de remoção.
Por que o NG0500 acontece com template que parece válido?
O aninhamento de HTML inválido é a surpresa comum: um <div> dentro de um <p>, um <a> dentro de um <a>, ou uma <table> sem <tbody>. O browser reescreve a marcação calado, então o DOM interpretado não bate mais com o HTML do servidor.
Funciona local mas o NG0500 estoura em produção. Por quê?
O HTML do SSR não pode ser alterado entre o servidor e o cliente. Um proxy ou CDN que minifica HTML, ou uma camada de edge que injeta ou reordena marcação, muda a árvore que o browser recebe. O NG0507 é o código explícito pra HTML alterado depois do rendering.
Fontes consultadas
- https://angular.dev/errors/NG0500
- https://angular.dev/guide/hydration
- https://angular.dev/guide/incremental-hydration
- https://angular.dev/guide/ssr
- https://angular.dev/api/core/afterNextRender
- https://blog.angular.dev/announcing-angular-v21-57946c34f14b
Modern Angular Playbook
Este artigo é um play.
O Playbook do Angular Moderno reúne o diagnóstico, a matriz de adoção, onze jogadas e o plano de 30 dias. Grátis, em inglês e português.
Você recebe os dois PDFs por email, pela lista do Dojo IA.
André Ramosdisponível para vagas remotas, UTC−3Entre em contato →