Como Resolver o Hydration Error do Next.js [Guia Completo 2026]

スポンサーリンク

Como Resolver o Hydration Error do Next.js [Guia Completo 2026]

Está vendo de repente “Hydration failed because the initial UI does not match what was rendered on the server” ou “Text content does not match server-rendered HTML” ao desenvolver com Next.js? Este artigo fornece um guia completo atualizado para 2026, cobrindo as causas e soluções concretas para erros de hidratação, incluindo o ambiente mais recente do Next.js 15.


O Que É Este Erro? Sintomas Que Você Verá

O Hydration Error (erro de hidratação) do Next.js ocorre quando há uma incompatibilidade entre o HTML gerado pelo renderização do lado do servidor (SSR) e o HTML que o React tenta renderizar na primeira renderização do lado do cliente.

O Que É Hidratação?

Hidratação (Hydration) é o processo em que o React pega o HTML estático pré-renderizado do servidor e anexa manipuladores de eventos e estado para torná-lo “interativo” no navegador. É chamado de “hidratação” porque injeta interatividade — como adicionar água para dar vida a algo.

Mensagens de Erro Comuns

Aqui estão as mensagens de erro típicas que os desenvolvedores encontram no console ou navegador:

  • Hydration failed because the initial UI does not match what was rendered on the server.
  • Text content does not match server-rendered HTML.
  • Error: Hydration failed because the server rendered HTML didn't match the client.
  • Warning: Expected server HTML to contain a matching <div> in <p>.

Quando Ocorre?

Este erro aparece principalmente nas seguintes situações:

  • Um erro vermelho aparece no console do navegador durante o desenvolvimento do aplicativo Next.js
  • Partes de uma página não são renderizadas corretamente ou as interações não funcionam em produção
  • Frequentemente é detectado pela primeira vez durante a pré-visualização pós-build
  • Extensões do Chrome que modificam o DOM também podem desencadeá-lo

Em termos de impacto, quando um erro de hidratação ocorre, o React tenta reconstruir completamente o DOM no lado do cliente, o que pode degradar o desempenho. Além disso, no Next.js 15 e versões posteriores (React 19), os erros de hidratação são tratados de forma mais rigorosa — o que antes eram avisos agora podem ser lançados como erros.


Por Que Este Erro Ocorre?

Os erros de hidratação têm muitas causas, mas podem ser amplamente categorizados nos seguintes cinco grupos.

Causa 1: Uso de APIs Exclusivas do Navegador (window, localStorage, navigator)

Como o ambiente do navegador não existe no lado do servidor, referenciar diretamente objetos exclusivos do navegador como window, localStorage, navigator ou document na lógica de renderização produz saídas diferentes no servidor e no cliente.

// ❌ ERRADO: window não existe no servidor, causando incompatibilidade
function MyComponent() {
  const width = window.innerWidth; // Erro ou undefined no servidor
  return <div>{width > 768 ? 'Desktop' : 'Mobile'}</div>;
}

Um padrão típico é usar a ramificação condicional typeof window !== 'undefined' na renderização, que retorna JSX diferente no servidor e no cliente, causando o erro.

Causa 2: Uso de Datas, Horários e Valores Aleatórios

Funções que retornam valores diferentes a cada chamada — como new Date(), Date.now(), Math.random() e crypto.randomUUID() — produzem valores incompatíveis entre servidor e cliente quando usadas durante a renderização.

// ❌ ERRADO: Horários diferentes exibidos no servidor e no cliente
function Clock() {
  return <p>Hora atual: {new Date().toLocaleTimeString()}</p>;
}

Diferenças de fuso horário também são uma causa comum. Quando o servidor opera em UTC e o cliente está em um fuso horário local, o mesmo objeto Date produz strings diferentes.

Causa 3: Aninhamento HTML Inválido

Quando a estrutura HTML viola regras de aninhamento (por exemplo, colocar um <div> dentro de uma tag <p>), o navegador corrige automaticamente o HTML, criando uma incompatibilidade entre o HTML enviado pelo servidor e o DOM interpretado pelo navegador.

// ❌ ERRADO: <div> não pode ir dentro de <p>
function BadNesting() {
  return (
    <p>
      Texto
      <div>Esta parte é o problema</div>
    </p>
  );
}

Causa 4: Extensões do Chrome Modificando o DOM

Em 2026, uma das causas mais frustrantes para muitos desenvolvedores são as extensões do Chrome. Extensões como ColorZilla, Grammarly, Google Translate e gerenciadores de senhas adicionam atributos ou elementos ao DOM antes que a hidratação do React comece, causando incompatibilidades com o HTML do servidor.

Especialmente em ambientes Next.js 15 + React 19, atributos injetados por extensões como cz-shortcut-listen="true" foram relatados como causas diretas de erros de hidratação.

Causa 5: Scripts de Terceiros e Interferência de Widgets

Scripts de terceiros como Google Analytics, ferramentas de testes A/B, widgets de chat e scripts de anúncios podem modificar o DOM antes da hidratação do React, provocando erros.


Solução 1: Renderização Exclusiva do Cliente com useEffect (Recomendada)

A solução mais recomendada é usar o hook useEffect para definir valores apenas no lado do cliente. Como o useEffect é executado após a hidratação ser concluída, ele mantém a consistência do HTML entre servidor e cliente.

Passo 1: Padrão de Combinação state e useEffect

Quando você precisa de valores específicos do navegador (largura da tela, valores do localStorage, etc.), defina o valor inicial para corresponder ao servidor, depois atualize para valores específicos do cliente dentro do useEffect.

'use client';
import { useState, useEffect } from 'react';

function ResponsiveComponent() {
  // Passo 1: Definir valor inicial igual ao do servidor
  const [isMobile, setIsMobile] = useState(false);

  // Passo 2: Definir valor específico do cliente no useEffect
  useEffect(() => {
    setIsMobile(window.innerWidth < 768);

    const handleResize = () => setIsMobile(window.innerWidth < 768);
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return (
    <div>
      {isMobile ? <MobileNav /> : <DesktopNav />}
    </div>
  );
}

Passo 2: Padrão de Rastreamento do Estado de Montagem

Um padrão mais versátil rastreia se o componente foi montado no lado do cliente.

'use client';
import { useState, useEffect } from 'react';

function ClientOnlyComponent() {
  const [isMounted, setIsMounted] = useState(false);

  useEffect(() => {
    setIsMounted(true);
  }, []);

  if (!isMounted) {
    return <div className="skeleton">Carregando...</div>;
  }

  return (
    <div>
      <p>Hora atual: {new Date().toLocaleTimeString()}</p>
      <p>Navegador: {navigator.userAgent}</p>
    </div>
  );
}

Passo 3: Criar um Hook Personalizado Reutilizável

Recomendamos criar um hook personalizado para reutilizar em todo o projeto.

// hooks/useHydration.ts
'use client';
import { useState, useEffect } from 'react';

export function useHydrated() {
  const [hydrated, setHydrated] = useState(false);

  useEffect(() => {
    setHydrated(true);
  }, []);

  return hydrated;
}

// Uso
function MyComponent() {
  const hydrated = useHydrated();

  if (!hydrated) return <Skeleton />;

  return <div>{/* Conteúdo específico do cliente */}</div>;
}

Notas Importantes

  • É crucial manter a mesma saída do servidor durante a renderização inicial do useEffect
  • Use UI de esqueleto ou indicadores de carregamento para minimizar a degradação da experiência do usuário
  • Esta abordagem pode afetar o SEO, então aplique-a com cuidado ao conteúdo que deseja que os mecanismos de busca indexem

Solução 2: Desativar SSR com Importação Dinâmica

Quando o padrão useEffect é inconveniente ou quando todo o componente é exclusivo do cliente, você pode desativar o SSR usando o dynamic() do Next.js.

Uso Básico

import dynamic from 'next/dynamic';

const ClientOnlyChart = dynamic(
  () => import('../components/Chart'),
  {
    ssr: false,
    loading: () => <p>Carregando gráfico...</p>
  }
);

export default function Dashboard() {
  return (
    <div>
      <h1>Painel</h1>
      <ClientOnlyChart />
    </div>
  );
}

Uso com App Router

Com o Next.js 13+ App Router, o dynamic funciona da mesma forma.

// app/dashboard/page.tsx
import dynamic from 'next/dynamic';

const MapComponent = dynamic(
  () => import('@/components/Map'),
  { ssr: false }
);

export default function DashboardPage() {
  return (
    <main>
      <h1>Visualização do Mapa</h1>
      <MapComponent />
    </main>
  );
}

Solução 3: Corrigir Estrutura HTML e Lidar com Extensões do Navegador (Avançado)

Corrigir Problemas de Aninhamento HTML

// ❌ ERRADO: <div> não pode ir dentro de <p>
<p>Texto<div>Elemento de bloco</div></p>

// ✅ CORRETO: Envolver com <div> ou usar <span>
<div>
  <p>Texto</p>
  <div>Elemento de bloco</div>
</div>

// ✅ CORRETO: Usar elementos inline
<p>Texto<span>Elemento inline</span></p>

Lidar com Extensões do Chrome

Método A: Restrinja o acesso da extensão ao site. Clique com o botão direito no ícone da extensão do Chrome e altere “Ler e alterar dados do site” para “Quando você clicar na extensão”.

Método B: Crie um perfil de navegador específico para desenvolvimento sem extensões.

Método C: Use suppressHydrationWarning seletivamente.

// app/layout.tsx
export default function RootLayout({ children }) {
  return (
    <html lang="pt">
      <body suppressHydrationWarning={true}>
        {children}
      </body>
    </html>
  );
}

Como Prevenir Erros de Hidratação

1. Evite Código Dependente do Ambiente Durante a Renderização

Não use código que gere valores diferentes dependendo do ambiente na instrução return do componente. Mova toda a lógica dependente do ambiente para o useEffect.

2. Aproveite as CSS Media Queries

.mobile-only { display: none; }
.desktop-only { display: block; }

@media (max-width: 768px) {
  .mobile-only { display: block; }
  .desktop-only { display: none; }
}

3. Automatize a Validação HTML

Use o plugin jsx-a11y do ESLint e as regras do eslint-plugin-react para detectar aninhamentos HTML inválidos no momento do build.

4. Estabeleça um Ambiente de Testes

  • Teste regularmente com next build && next start
  • Teste com um perfil de navegador limpo sem extensões
  • Monitore erros de hidratação em produção usando ferramentas como Sentry

5. Otimize a Localização de Scripts de Terceiros

import Script from 'next/script';

<Script src="https://example.com/analytics.js" strategy="afterInteractive" />
<Script src="https://example.com/widget.js" strategy="lazyOnload" />

Resumo

O Hydration Error do Next.js é causado por incompatibilidades entre os resultados de renderização do servidor e do cliente. No ambiente Next.js 15 + React 19 de 2026, essas verificações são mais rigorosas do que antes, tornando a compreensão e o tratamento adequados cada vez mais importantes.

Pontos-Chave:

  1. O padrão useEffect é a solução mais eficaz para a maioria dos casos
  2. Dynamic import (ssr: false) deve ser usado quando todo o componente depende do cliente
  3. Sempre cuide da estrutura HTML correta
  4. Para problemas com extensões do Chrome, use um perfil de desenvolvimento ou suppressHydrationWarning
  5. Priorize CSS Media Queries em vez de ramificação condicional em JavaScript

Referências

コメント

タイトルとURLをコピーしました