Next.js: Corrigir 'Hydration failed because the initial UI does not match'
Se você já encontrou o temido erro “Hydration failed because the initial UI does not match what was rendered on the server” na sua aplicação Next.js, você não está sozinho. Este erro de incompatibilidade de hidratação do React é um dos problemas mais comuns que desenvolvedores enfrentam ao construir aplicações renderizadas no servidor. Vamos esclarecer a confusão e corrigi-lo adequadamente.
Pontos-Chave
- Erros de hidratação ocorrem quando servidor e cliente renderizam HTML diferente
- APIs exclusivas do navegador, valores aleatórios e aninhamento HTML inválido são culpados comuns
- Use useEffect para lógica client-side, importações dinâmicas para componentes client-only, ou garanta renderização determinística
- Mantenha as renderizações iniciais idênticas entre servidor e cliente
O Que É Hidratação e Por Que Ela Quebra?
Hidratação é o processo do React de anexar funcionalidade JavaScript ao HTML renderizado no servidor. Quando o Next.js envia HTML pré-renderizado para o navegador, o React assume comparando o HTML do servidor com o que ele renderizaria no cliente. Se não corresponderem, você recebe um erro de hidratação.
Pense nisso como o React verificando o trabalho do servidor. Quando as renderizações iniciais diferem, o React não pode anexar com segurança manipuladores de eventos e gerenciamento de estado, acionando a necessidade de uma correção de hidratação.
Causas Comuns de Erros de Hidratação no Next.js
APIs Exclusivas do Navegador Quebrando o SSR
O culpado mais frequente em problemas de renderização server-side do React é usar APIs do navegador durante a renderização inicial:
// ❌ Isso quebra - window não existe no servidor
function BadComponent() {
const width = window.innerWidth;
return <div>Screen width: {width}px</div>;
}
Valores Não-Determinísticos
Valores aleatórios ou timestamps criam saídas diferentes entre servidor e cliente:
// ❌ Servidor e cliente gerarão IDs diferentes
function RandomComponent() {
return <div id={Math.random()}>Content</div>;
}
Diferenças na Renderização Condicional
Quando sua lógica produz estruturas HTML diferentes:
// ❌ Estado mounted difere entre servidor (false) e cliente (true após effect)
function ConditionalComponent() {
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
return <div>{mounted ? 'Client' : 'Server'}</div>;
}
Aninhamento HTML Inválido
Estrutura HTML incorreta que navegadores corrigem automaticamente:
<!-- ❌ Aninhamento inválido -->
<p>
<div>This breaks hydration</div>
</p>
Discover how at OpenReplay.com.
Três Correções Confiáveis para Erros de Hidratação
Correção 1: Envolver Lógica Client-Only com useEffect
O hook useEffect executa após a hidratação ser concluída, tornando-o seguro para código específico do navegador:
function SafeComponent() {
const [screenWidth, setScreenWidth] = useState(0);
useEffect(() => {
// Isso só executa no cliente após a hidratação
setScreenWidth(window.innerWidth);
}, []);
// Retorna renderização inicial consistente
if (screenWidth === 0) return <div>Loading...</div>;
return <div>Screen width: {screenWidth}px</div>;
}
Correção 2: Desabilitar SSR com Importações Dinâmicas
Para componentes que dependem fortemente de APIs do navegador, pule completamente a renderização no servidor:
import dynamic from 'next/dynamic';
const ClientOnlyComponent = dynamic(
() => import('./BrowserComponent'),
{
ssr: false,
loading: () => <div>Loading...</div> // Previne mudança de layout
}
);
export default function Page() {
return <ClientOnlyComponent />;
}
Correção 3: Garantir Renderização Determinística
Gere valores estáveis que permanecem consistentes entre renderizações:
// ✅ Use IDs estáveis de props ou gere-os uma vez
function StableComponent({ userId }) {
// Use um ID determinístico baseado em props
const componentId = `user-${userId}`;
return <div id={componentId}>Consistent content</div>;
}
// Para valores verdadeiramente aleatórios, gere no servidor
export async function getServerSideProps() {
return {
props: {
sessionId: generateStableId() // Gera uma vez no servidor
}
};
}
Depurando Problemas de SSR no Next.js
Ao lidar com depuração de SSR no Next.js, use estas técnicas:
- Habilite avisos estritos de hidratação do React em desenvolvimento
- Compare HTML do servidor e cliente usando DevTools do navegador
- Adicione console logs para identificar qual componente causa a incompatibilidade
- Use React DevTools para inspecionar a árvore de componentes
// Auxiliar de depuração temporário
useEffect(() => {
console.log('Component hydrated:', typeof window !== 'undefined');
}, []);
Melhores Práticas para Prevenir Futuros Erros de Hidratação
Mantenha renderizações iniciais idênticas: O servidor e cliente devem produzir o mesmo HTML na primeira renderização. Deixe atualizações dinâmicas para depois da hidratação.
Valide a estrutura HTML: Use aninhamento adequado e elementos HTML válidos. Ferramentas como o W3C Validator podem detectar problemas cedo.
Teste com JavaScript desabilitado: Seu conteúdo renderizado no servidor deve ser funcional sem JavaScript, garantindo uma base sólida.
Use TypeScript: A verificação de tipos ajuda a detectar potenciais incompatibilidades durante o desenvolvimento em vez de em tempo de execução.
Conclusão
Erros de incompatibilidade de hidratação do React no Next.js são frustrantes, mas previsíveis uma vez que você entende o padrão. A chave é garantir que seu servidor e cliente produzam HTML inicial idêntico. Seja usando useEffect para lógica client-side, desabilitando SSR com importações dinâmicas, ou garantindo renderização determinística, a solução sempre volta a este princípio: mantenha a primeira renderização consistente, depois adicione recursos client-side.
Lembre-se: se seu código depende do ambiente do navegador, mantenha-o fora do caminho de renderização inicial. Suas aplicações Next.js hidratarão suavemente, e seus usuários nunca saberão da complexidade acontecendo nos bastidores.
Perguntas Frequentes
Sim, mas não é recomendado. Avisos de hidratação indicam problemas reais que podem causar inconsistências na UI. Em vez de suprimi-los, corrija a causa raiz usando useEffect ou importações dinâmicas para garantir funcionalidade adequada.
Datas frequentemente renderizam diferentemente no servidor e cliente devido a diferenças de fuso horário. Use um formato consistente convertendo datas para strings no servidor ou use uma biblioteca como date-fns com fusos horários fixos para ambos os ambientes.
Carregue scripts de terceiros após a hidratação usando o componente Script do Next.js com strategy afterInteractive ou lazyOnload. Para componentes que dependem desses scripts, envolva-os em importações dinâmicas com ssr false.
Desabilitar SSR significa que esses componentes não aparecerão no HTML inicial, potencialmente afetando SEO e aumentando o tempo até interatividade. Use com moderação para recursos verdadeiramente dependentes do cliente e forneça estados de carregamento para prevenir mudanças de layout.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.