14 nov 2025·6 min de lectura

Lista de verificación de desajustes de hidratación en Next.js para interfaces generadas por IA

Usa esta lista de verificación de desajustes de hidratación en Next.js para detectar rápidamente diferencias entre servidor y cliente: fechas, aleatoriedad, acceso a window y renderizados inestables.

Lista de verificación de desajustes de hidratación en Next.js para interfaces generadas por IA

Qué es realmente un desajuste de hidratación (en lenguaje sencillo)

Con el renderizado en servidor, Next.js envía HTML primero para que la página aparezca rápido. Luego React se ejecuta en el navegador y “hidrata” ese HTML adjuntando manejadores de eventos y renderizando la misma UI otra vez usando las mismas entradas.

Un desajuste de hidratación ocurre cuando el HTML del servidor no coincide con lo que React produce en la primera renderización del cliente. React advierte porque no puede asumir de forma segura que el DOM es consistente.

En la práctica, puede verse como una advertencia en la consola, un parpadeo rápido cuando React reemplaza partes de la página, botones que no responden por un momento o texto que cambia justo después de la carga. A veces es sutil. Otras veces rompe formularios, la UI de autenticación y cualquier cosa que dependa de un DOM estable.

El código de UI generado por IA suele provocar esto con más frecuencia porque tiende a mezclar valores solo del navegador en el render sin protecciones. Ejemplos típicos incluyen leer desde window durante el render, formatear fechas de forma distinta en servidor y cliente, o usar aleatoriedad para elegir un color, ID o valor por defecto.

Regla simple: si el servidor y el navegador pueden producir respuestas distintas, no uses ese valor directamente en el render.

Qué puedes normalmente ignorar vs qué debes arreglar:

  • Riesgo bajo normalmente: una pequeña diferencia en className que no afecta el layout ni la entrada del usuario.
  • Arreglar rápido: cualquier cosa que cause que el contenido salte, que inputs se reinicien, que el estado de auth cambie o que elementos desaparezcan.
  • Arreglar siempre: cualquier cosa ligada a identidad o seguridad (por ejemplo, mostrar un estado de usuario incorrecto).

Triage rápido: reproducir y aislar el desajuste

Una advertencia de hidratación es específica: el servidor envió HTML, luego el navegador intentó adjuntar React, y la primera renderización del cliente no coincidió con lo que ya estaba en la página. Antes de perseguirla, asegúrate de que no estás viendo en realidad una falla de fetch (datos vacíos), un redirect o un cambio de layout que solo parece un problema de hidratación.

Reprodúcelo en una build de producción. El modo dev puede añadir renders extra y advertencias que confunden el origen.

Un flujo rápido de triage:

  • Ejecuta una build de producción, inicia la app localmente y recarga la página que lanza la advertencia.
  • Prueba tanto una recarga completa (first load) como una navegación del lado del cliente a la misma ruta.
    • Falla solo al recargar: sospecha diferencias en la salida SSR.
    • Falla solo después de la navegación: sospecha estado del cliente o efectos.
  • Lee la pista exacta en la advertencia de React (a menudo apunta a un elemento específico, un nodo de texto o un atributo).
  • Temporalmente elimina trozos de la página hasta que la advertencia desaparezca, luego agrégalos de nuevo hasta encontrar el componente más pequeño que aún falla.
  • Una vez tengas el componente más pequeño que falla, compara lo que renderiza en el servidor vs el cliente. Un solo carácter de diferencia basta.

Mientras está fresco, anota:

  • Ruta y pasos exactos (recarga vs navegación)
  • El elemento o texto que React menciona
  • Si el desajuste es contenido (texto), atributos (class, style) o estructura (envoltorio extra)
  • Si los datos están presentes en la primera pintura o aparecen después

Un patrón común en widgets de IA: un encabezado “Welcome, Sam” renderiza en el servidor como “Welcome” (sin usuario aún), luego el cliente rellena el nombre desde localStorage. Eso es un desajuste, y “recarga vs navegación” normalmente lo deja claro.

Comprobaciones rápidas de 10 minutos para desajustes

Las victorias más rápidas vienen de encontrar cualquier cosa que pueda renderizarse distinto en el servidor que en el navegador. No estás arreglando todo todavía. Estás identificando la entrada que cambia entre el HTML del servidor y la primera renderización del cliente.

Un escaneo rápido que atrapa la mayoría de bugs generados por IA

Abre el componente nombrado en la advertencia (o la página recordada) y busca los sospechosos habituales:

Date, Math.random, window, document, navigator, localStorage, sessionStorage.

Una rutina simple:

  • Reduce la página al componente más pequeño que aún dispare la advertencia.
  • Busca valores dinámicos usados en texto JSX, atributos o props (hora, números aleatorios, comprobaciones de viewport).
  • Comprueba renderizado condicional basado en viewport, user agent o media queries en JavaScript (no en CSS).
  • Busca key inestables, IDs generados o nombres de clase que cambien entre ejecuciones.
  • Hardcodea el valor sospechoso (por ejemplo, una cadena de fecha fija) para confirmar que la advertencia desaparece.

Elegir el arreglo más seguro

La mayoría de arreglos encajan en algunos patrones:

  • Hacer el render determinista: calcula el valor en el servidor y pásalo como prop.
  • Demorar el valor hasta después del montaje: renderiza un placeholder primero y luego ajusta estado en useEffect.
  • Proteger APIs solo de navegador: comprobar typeof window !== "undefined" antes de leerlas.
  • Mover un widget a client-only cuando realmente dependa del estado del navegador.

Lista de verificación: fechas, zonas horarias y formateo de locale

Las fechas son una causa principal de problemas de hidratación porque el servidor y el navegador pueden discrepar sobre el “tiempo actual”, la zona horaria o los valores por defecto de locale. Si el texto renderizado difiere aunque sea ligeramente, React se quejará.

Comprobaciones rápidas

Busca UI que transforme “ahora” en texto durante el render:

  • new Date() o Date.now() dentro del render del componente
  • Intl.DateTimeFormat(...) sin timeZone y locale fijos
  • Tiempo relativo como “hace un momento” calculado durante SSR
  • Temporizadores o cuentas regresivas que dependan del segundo actual
  • Servidor usando UTC mientras el navegador usa zona horaria local

Un patrón común es:

“Last updated: {new Date().toLocaleString()}”

Eso casi siempre diferirá entre servidor y cliente.

Patrones de corrección que mantienen la página estable

Elige el enfoque que coincida con lo que los usuarios realmente necesitan:

  • Pasa la marca de tiempo como dato y renderiza ese valor exacto.
  • Formatea con una zona horaria explícita (a menudo UTC) y una locale escogida.
  • Si necesitas tiempo relativo, renderiza un placeholder estable en el servidor y calcula el relativo después del montaje.
  • Para relojes en vivo o cuentas regresivas, renderiza un valor inicial desde el servidor y empieza a actualizar en un efecto.

Lista de verificación: aleatoriedad y renderizado no determinista

Otra causa común es simple: el servidor renderiza una versión y luego el navegador renderiza otra porque el código “aleatorio” se ejecutó de nuevo.

Comprueba primero:

  • Math.random() usado para IDs, colores, "elegir una carta", avatares o variantes
  • Ordenar o mezclar dentro del render (incluido items.sort(...) que muta)
  • Keys creadas al vuelo (key={Math.random()}, key={Date.now()})
  • Generadores de contenido de muestra que devuelven texto distinto en cada ejecución

Haz la primera renderización determinista:

  • Precalcula en el servidor y pásalo como prop.
  • Usa keys estables de IDs reales (o índices solo para listas realmente estáticas).
  • Mueve la aleatoriedad a después del montaje (useEffect) para que solo afecte actualizaciones cliente.

Ejemplo: un widget “Plantillas destacadas” mezcla plantillas durante el render y usa índices mezclados como keys. El servidor renderiza orden A, el cliente orden B, y falla la hidratación. Premezcla una vez (en el servidor) o inicializa estado desde una lista proporcionada por el servidor, luego keyea por un ID de plantilla estable.

Lista de verificación: APIs solo de navegador y comprobaciones de entorno

Obtén una auditoría gratuita
Recibe una auditoría de código gratuita y una lista clara de problemas antes de comprometerte con trabajo.

Los desajustes suelen ocurrir cuando el servidor renderiza una versión, pero el navegador inmediatamente renderiza otra porque tu código lee valores solo disponibles en el navegador demasiado pronto.

Qué buscar

Escanea en busca de APIs de navegador usadas durante el render (incluyendo helpers llamados en el render): window, document, localStorage, sessionStorage, navigator.

También vigila UI que cambie de estructura según el tamaño de pantalla. Si lees window.innerWidth o matchMedia() para decidir qué árbol de componentes renderizar (no solo estilos), el servidor está adivinando y a veces fallará.

Patrones de corrección más seguros

Mantén la primera renderización determinista y luego actualiza después del montaje:

  • Protege el acceso al navegador: if (typeof window !== "undefined") { ... }
  • Mueve lecturas de navegador a useEffect (o useLayoutEffect solo cuando sea realmente necesario)
  • Renderiza un placeholder estable en el servidor y luego reemplázalo en el cliente
  • Prefiere CSS para cambios responsivos en lugar de renderizado condicional
  • Si la característica depende totalmente del estado del navegador, hazla client-only y deja la salida del servidor mínima

Lista de verificación: carga de datos y desajustes de estado de auth

Estos desajustes ocurren cuando el servidor renderiza una “primera vista”, pero el navegador la reemplaza inmediatamente con otra basada en datos en caché o estado de autenticación.

Disparadores comunes:

  • El servidor renderiza “0 items”, luego el cliente hidrata con items cacheados desde localStorage o IndexedDB.
  • El servidor piensa que el usuario no está logueado, pero el cliente lee un token y muestra UI de usuario logueado.
  • Flags de funcionalidades que se evalúan de forma distinta (valores por defecto en servidor vs preferencias guardadas).

Patrones de corrección que mantienen la primera pintura consistente:

  • Usa una carcasa de carga consistente (mismo marcado en servidor y primera renderización del cliente), luego intercambia por los datos reales.
  • Pasa datos iniciales y estado de auth desde el servidor para que el cliente empiece desde el mismo estado.
  • Retrasa la lectura de caches de navegador hasta después de la hidratación y renderiza un placeholder hasta entonces.
  • Mantén los valores por defecto de flags idénticos en servidor y cliente, luego aplica los overrides del usuario después de la hidratación.

Lista de verificación: estilos, layout y renderizado responsivo

Depura la causa raíz
Aislamos el componente mínimo que falla y lo parcheamos correctamente.

Algunos problemas de hidratación son “mismos datos, diferente estructura”. El servidor renderiza una forma del DOM y el navegador renderiza otra después de conocer el tamaño de pantalla, fuentes o mediciones.

Lógica responsiva que cambia el DOM

Si tu UI renderiza árboles de componentes diferentes por breakpoint (por ejemplo, “menú móvil” vs “pestañas de escritorio”), el servidor tiene que adivinar.

Prefiere renderizar el mismo DOM en ambos lados y luego cambiar la presentación con CSS. Si debes cambiar el marcado, haz que solo cambie después del montaje.

CSS-in-JS y orden de className

Una SSR mal configurada para algunos setups de estilos puede producir nombres de clase o un orden de inserción distinto entre servidor y cliente. Si la advertencia menciona diferencias en className o ves un flash de estilos, confirma que estás usando la configuración SSR documentada para tu librería de estilos y evita generar estilos a partir de valores no deterministas.

Mediciones de layout y carga de fuentes

Mediciones en tiempo de render como getBoundingClientRect() no pueden coincidir en el servidor. Mide en useEffect, renderiza un placeholder estable primero y aplica cambios dependientes del layout después del montaje.

Paso a paso: las maneras más seguras de estabilizar páginas

El objetivo es claro: el primer HTML que envía el servidor debe coincidir con lo que el navegador renderiza antes de que se ejecuten los efectos.

Una secuencia fiable para estabilizar:

  1. Identifica qué debe coincidir. Concéntrate en el nodo exacto que React señala.

  2. Haz la salida del servidor determinista. Precalcula valores en el servidor y pásalos como props. Evita llamar new Date() durante el render.

  3. Difere la lógica solo de navegador. Todo lo que necesite window, document, localStorage, tamaño de pantalla o ajustes de usuario debe empezar con marcado estable y actualizarse en useEffect, o vivir por completo en un Componente Cliente.

  4. Aísla widgets riesgosos. Si un componente realmente depende de APIs de navegador o valores no deterministas, cárgalo solo en el cliente para que el resto de la página permanezca estable:

import dynamic from "next/dynamic";

const ClientOnlyWidget = dynamic(() => import("./Widget"), { ssr: false });
  1. Usa suppressHydrationWarning solo como último recurso. Limítalo a texto pequeño y seguro donde una diferencia puntual sea aceptable. No lo uses para ocultar desajustes en UI interactiva o contenido condicionado por auth.

Errores comunes que hacen que el desajuste vuelva

Las advertencias de hidratación suelen desaparecer tras un parche, pero vuelven cuando alguien añade una nueva insignia, banner o comprobación de autenticación.

Los “arreglos” que causan dolor a largo plazo:

  • Desactivar SSR para una página o layout entero cuando solo un widget pequeño es inestable.
  • Renderizar “ahora” directamente en JSX (timestamps, etiquetas “hace un momento”).
  • Crear keys a partir de valores aleatorios o tiempo.
  • Usar useLayoutEffect para cambios solo de navegador sin un plan client-only.
  • Tratar la supresión como la solución principal.

Si el marcado cambia según isLoggedIn antes de que el cliente conozca la sesión, renderiza primero una carcasa neutral y luego intercambia cuando la auth esté confirmada.

Ejemplo realista: un widget generado por IA que rompe la hidratación

Encuentra el desajuste rápido
Envía tu repositorio y localizaremos la divergencia exacta servidor vs cliente que causa la advertencia.

Una tarjeta típica del dashboard muestra algo como “Updated 12 seconds ago” y lo calcula usando Date.now(), la locale del usuario y a veces una zona horaria preferida desde localStorage.

Esa es la receta perfecta para un desajuste: el servidor renderiza una cadena (hora del servidor, locale del servidor, sin localStorage), luego el navegador renderiza otra cadena diferente (hora local del cliente, locale del cliente, ajustes guardados).

Aquí tienes una reescritura más segura que mantiene la primera renderización estable y luego actualiza tras la hidratación:

function UpdatedLabel({ updatedAt, initialNow, locale }: {
  updatedAt: number
  initialNow: number
  locale: string
}) {
  const [text, setText] = React.useState(() =>
    formatRelative(initialNow, updatedAt, locale)
  )

  React.useEffect(() => {
    const id = window.setInterval(() => {
      setText(formatRelative(Date.now(), updatedAt, locale))
    }, 1000)
    return () => window.clearInterval(id)
  }, [updatedAt, locale])

  return <span>{text}</span>
}

Idea clave: el servidor y el cliente comparten el mismo initialNow y locale para la primera pintura, así que el marcado coincide. Solo entonces el cliente comienza a actualizar.

Para validar, prueba las situaciones que provocan divergencia:

  • Build de producción (no modo dev)
  • Recarga completa con la caché deshabilitada
  • Diferente zona horaria o locale
  • Sesión de incógnito (sin ajustes guardados)

Verificación final y cuándo pedir ayuda

Después de un cambio, prueba como si quisieras romperlo:

  • Recarga completa (no solo navegación del cliente)
  • Ventana de incógnito (sin datos en caché, menos extensiones)
  • Throttling de red lento
  • Diferente idioma del navegador o zona horaria

También ayuda mantener una nota corta de “reglas SSR” cerca del código UI que tiende a regenerarse: no window en render, no Math.random() en marcado, no formateo de fechas sin zona horaria explícita y nada dependiente de auth hasta que el estado de auth sea conocido.

Si aún ves desajustes tras las correcciones obvias, el código normalmente está peleando contigo. Eso es común en prototipos generados por herramientas como Lovable, Bolt, v0, Cursor o Replit. Los equipos a veces recurren a FixMyMess (fixmymess.ai) para una auditoría rápida que localice la divergencia exacta servidor/cliente y repare las partes inestables sin desactivar SSR para todo.

Preguntas Frecuentes

¿Qué es un desajuste de hidratación en Next.js, en palabras sencillas?

Un desajuste de hidratación ocurre cuando el HTML que Next.js envía desde el servidor no coincide con lo que React renderiza en el cliente durante la primera renderización. React avisa porque no puede adjuntar de forma segura manejadores de eventos a un DOM que no creó.

A menudo notarás una advertencia en la consola, un breve parpadeo o una UI que cambia justo después de cargar.

¿Cómo sé si el problema está relacionado con SSR o con el estado del cliente?

Empieza por reproducirlo en una compilación de producción, no en modo desarrollo. Luego compara una recarga completa (hard refresh) frente a una navegación del lado del cliente a la misma ruta.

Si solo falla al recargar, normalmente es una diferencia SSR vs primera renderización del cliente. Si falla principalmente después de una navegación, suele ser estado del cliente, efectos o datos en caché.

¿Cuáles son las cosas más rápidas que debo buscar cuando veo una advertencia de hidratación?

Busca cualquier cosa que pueda producir salida diferente en servidor y navegador durante la renderización: new Date(), Date.now(), Math.random(), formateo de locale, window, document, navigator, localStorage y sessionStorage.

Si alguno de esos valores afecta texto, atributos o qué elementos se renderizan, tienes un candidato fuerte para el desajuste.

¿Por qué las fechas y zonas horarias causan tantos desajustes de hidratación?

Porque el servidor y el navegador pueden estar en desacuerdo sobre “ahora”, la zona horaria y la locale por defecto. Incluso una sola diferencia de carácter en una marca de tiempo formateada es suficiente para activar la advertencia.

La opción más segura es renderizar una marca de tiempo estable a partir de los datos (o un valor conocido proporcionado por el servidor) y calcular el “tiempo relativo” solo después de que el componente monte.

¿Por qué `Math.random()` en JSX rompe la hidratación?

Porque se ejecuta dos veces: una en el servidor y otra en el navegador. Si llamas a Math.random() durante la renderización para elegir un ID, color, variante o key, el servidor y el cliente probablemente escogerán resultados distintos.

Haz la primera renderización determinista usando IDs estables de tus datos, o mueve la aleatoriedad a una actualización posterior al montaje.

¿Cuál es la manera correcta de usar `window` o `localStorage` sin causar un desajuste?

Leer APIs solo de navegador durante la renderización hace que el cliente produzca salida distinta a la del servidor, porque el servidor no puede leer esos valores. Un ejemplo común es mostrar UI “logueado” basándose en un token de localStorage.

Una solución práctica es renderizar una carcasa neutra y estable en el servidor y poblar el estado derivado del navegador en useEffect después de la hidratación.

¿Cómo evito desajustes con el estado de autenticación (UI logueado vs deslogueado)?

Si el servidor renderiza “desconectado” pero el cliente inmediatamente muestra “conectado” al leer un token, React verá un marcado distinto. Eso también puede hacer que los inputs se reinicien o que botones no funcionen temporalmente.

La forma limpia es hacer que el servidor y la primera renderización del cliente coincidan pasando el estado de sesión inicial desde el servidor cuando sea posible, o renderizando un estado de carga/esqueleto consistente hasta confirmar la autenticación.

¿Puede la lógica de UI responsiva causar desajustes de hidratación?

Sí. Si utilizas window.innerWidth o matchMedia() durante la renderización para elegir entre dos árboles de componentes, el servidor está adivinando el tamaño de pantalla del usuario.

Prefiere renderizar el mismo DOM en ambos lados y usa CSS para cambiar la presentación. Si debes cambiar el marcado, hazlo después del montaje para que la primera renderización sea estable.

¿Cuándo es aceptable `suppressHydrationWarning` y cuándo es arriesgado?

Usarlo solo para texto pequeño no interactivo donde una diferencia puntual sea aceptable. Básicamente le dices a React “no avises aquí”, no haces que la UI sea realmente consistente.

Evítalo en campos de formularios, UI protegida por autenticación o cualquier cosa que afecte identidad, permisos o acciones del usuario.

¿Qué hago si no encuentro el desajuste en una base de código Next.js generada por IA?

Si has eliminado las causas obvias y la advertencia sigue moviéndose, es probable que la base de código tenga múltiples fuentes de divergencia servidor/cliente. Eso es habitual en prototipos generados por IA donde lecturas solo de navegador, keys aleatorias y formateo de fechas acaban mezcladas en caminos de render.

FixMyMess puede ejecutar una auditoría rápida para localizar el componente y el valor exacto que provocan el desajuste, y repararlo sin desactivar SSR por completo, generalmente en 48–72 horas.