Optimización por plataforma

Cómo mejorar Core Web Vitals en Next.js con renderizado, imágenes e hidratación

Decide qué envía el servidor, qué ejecuta el navegador y qué representa realmente cada medición.

Un árbol de componentes distingue los nodos renderizados en el servidor de los nodos cliente interactivos.

Para mejorar Core Web Vitals en Next.js, entrega pronto el contenido importante, limita el JavaScript del navegador y reserva espacio para los elementos que llegan después. Next.js ofrece herramientas útiles, pero la estrategia de renderizado y los límites entre componentes siguen determinando cuánto trabajo realiza el dispositivo del visitante. Diagnostica la métrica lenta antes de cambiar la configuración del framework.

Los ejemplos se dirigen a Next.js 16 con App Router y se contrastaron con documentación identificada como v16.4.2. Ilustran patrones de implementación, no resultados de pruebas comparativas. Las aplicaciones con Pages Router y las aplicaciones React sin Next.js necesitan otros puntos de integración, aunque los problemas del navegador sean los mismos.

Empieza por la ruta y la métrica

Elige una ruta real con un problema medible, no la página más fácil de optimizar. Registra el tipo de dispositivo, la navegación, la versión desplegada y el estado del usuario. Un artículo público en caché, un panel con sesión iniciada y una ficha de producto con recomendaciones personalizadas no comparten la misma ruta crítica.

LCP indica cuándo se pinta el contenido visible elegible de mayor tamaño. Una respuesta lenta del servidor, una solicitud tardía de imagen o el trabajo del hilo principal pueden retrasarlo. INP describe la capacidad de respuesta a lo largo de la visita. La hidratación, el controlador de un filtro y el renderizado posterior a un cambio de estado pueden influir en momentos diferentes. CLS mide movimientos inesperados, incluidos los posteriores a la primera pantalla.

Usa datos de campo para identificar a la población afectada y una grabación de rendimiento del navegador para explicar la demora. Una auditoría estándar de navegación de Lighthouse no permite establecer el INP de producción. Tampoco un clic rápido en el portátil del desarrollador demuestra que los usuarios reales disfruten de buena respuesta. La guía para diagnosticar INP separa las demoras de entrada, procesamiento y presentación.

Antes de editar, guarda una referencia breve: ruta, elemento LCP observado, tareas relevantes más largas, fuentes de desplazamiento y scripts cargados en esa ruta. Así conviertes una optimización general en una hipótesis comprobable. Si la imagen principal empieza a cargarse tarde, el primer cambio debe abordar su descubrimiento o prioridad, no limpiar paquetes sin relación.

Ajusta el renderizado a las necesidades reales del contenido

La generación estática, el renderizado por solicitud, el streaming y el renderizado cliente describen partes distintas de la entrega. Los Server Components describen dónde se ejecuta el código del componente. Un Server Component puede participar en una ruta estática o en trabajo realizado por solicitud; su nombre no determina la política de caché.

Estas son las principales compensaciones:

Enfoque Ventaja posible Riesgo que debes comprobar
HTML estático o en caché Contenido temprano sin repetir trabajo en origen Contenido obsoleto si falla la invalidación
Renderizado servidor por solicitud HTML actualizado o personalizado Dependencias de datos lentas que retrasan la respuesta
Streaming Secciones útiles antes de las más lentas El contenido LCP sigue esperando detrás de un límite
Contenido renderizado en cliente Vistas interactivas con datos bajo demanda Cascadas de scripts y datos antes del contenido útil

No elijas trabajo por solicitud solo porque la aplicación utiliza React. La documentación pública que cambia al publicarse puede prepararse con antelación. El contenido personalizado puede necesitar trabajo dinámico, pero la navegación y el texto descriptivo que lo rodean no tienen por qué esperar a todas esas dependencias.

El streaming se presta especialmente a interpretaciones incorrectas. Enviar antes un esqueleto puede mejorar la primera impresión sin adelantar el elemento LCP final. Examina cuándo llega el contenido real, no solo el primer byte del documento. Da al estado provisional y a su sustituto una geometría comparable para evitar saltos durante la entrega progresiva.

La caché depende de la versión de Next.js y de las funciones activadas. Revisa la ruta generada y su comportamiento desplegado en vez de copiar supuestos de versiones antiguas. Una sesión de desarrollo con recursos calientes no demuestra cómo responderá una solicitud fría en producción. Prueba cargas directas del documento y navegaciones cliente, porque pueden solicitar recursos diferentes.

Mantén límites cliente que puedas explicar

En App Router, las páginas y los layouts son Server Components de forma predeterminada. Usa Client Components cuando necesites estado, controladores de eventos y API del navegador. Sitúa 'use client' en el límite interactivo más pequeño que resulte útil, como un filtro o un control desplegable, en lugar de convertir todo un layout de contenido en código cliente por un botón.

El límite afecta al grafo de módulos importados. Una biblioteca pesada importada por ese módulo cliente puede entrar en el paquete del navegador aunque gran parte de la página parezca estática. Revisa las importaciones del componente interactivo y pásale solo los datos necesarios. Serializar un objeto grande como propiedades también tiene costes de transferencia y procesamiento aunque su transformación se ejecute en el servidor.

Un Client Component no equivale a HTML creado exclusivamente en el cliente. Next.js puede prerenderizar su salida inicial e hidratarla después en el navegador. La distinción importa cuando la página parece completa antes de que respondan sus controles: el HTML llegó, pero el JavaScript y los eventos necesarios aún pueden estar preparándose.

Analiza el paquete de producción para descubrir por qué una dependencia llega a una ruta. Después consulta una traza de rendimiento para determinar si su ejecución importa. El tamaño comprimido y el tiempo de CPU responden a preguntas diferentes. La guía sobre ejecución de JavaScript ayuda cuando las mejoras de descarga no reducen las tareas largas.

Define un plan de carga para la imagen LCP real

Usa next/image para proporcionar dimensiones y candidatos adaptables, y describe con precisión el ancho mostrado. Los valores intrínsecos width y height reservan la relación de aspecto; CSS controla el tamaño renderizado. Con fill, el contenedor necesita posicionamiento y un tamaño o relación de aspecto definidos. Una geometría incompleta puede convertir una mejora de imagen en un problema de diseño.

Este ejemplo de app/page.jsx presupone que existe public/images/storefront.webp y que es la imagen importante de la primera pantalla:

jsx
// app/page.jsx
import Image from 'next/image';

export default function Page() {
  return (
    <main style={{ maxWidth: 960, margin: '0 auto' }}>
      <h1>A guide to faster storefronts</h1>
      <Image
        src="/images/storefront.webp"
        alt="A storefront with a wide display window"
        width={1600}
        height={900}
        sizes="(max-width: 960px) 100vw, 960px"
        loading="eager"
        fetchPriority="high"
        style={{ width: '100%', height: 'auto' }}
      />
    </main>
  );
}

El valor de sizes corresponde al contenedor del ejemplo, que ocupa el ancho disponible hasta 960 píxeles CSS. Cámbialo cuando cambie el diseño. Una tarjeta de una cuadrícula con varias columnas no debe declarar todo el ancho de la ventana por comodidad. Comprueba el candidato seleccionado en el panel de red con anchos y densidades de píxeles representativos.

En Next.js 16, priority está obsoleto en favor de preload. Eso no significa que cada uso anterior de priority deba convertirse en una precarga incondicional. La referencia del componente Image suele recomendar carga inmediata o prioridad alta y desaconseja combinar preload con loading o fetchPriority.

Por eso el ejemplo combina carga inmediata y una indicación de prioridad, sin preload. La precarga es otra opción cuando una imagen crítica conocida necesita descubrirse antes. No precargues varios candidatos de portada para móvil y escritorio sin saber qué recurso utilizará el navegador. Mantén diferidas las imágenes secundarias fuera de pantalla y vuelve a identificar el elemento LCP real tras el cambio.

Comprueba también el servicio que entrega las imágenes. Las fuentes remotas necesitan una configuración adecuada de remotePatterns, y el optimizador predeterminado requiere un entorno de ejecución compatible. Una exportación estática necesita una estrategia explícita, como un loader personalizado o archivos previamente optimizados. La guía de optimización de imágenes explica las decisiones de formato, dimensiones y calidad que el componente no resuelve por sí solo.

Decide las fuentes junto con la estabilidad visual

next/font puede servir archivos de fuentes desde el despliegue de la aplicación. Con next/font/google, el navegador del visitante no necesita solicitarlos a Google. Con next/font/local, proceden del proyecto. Ninguna opción elimina la necesidad de revisar glifos, pesos, métricas de sustitución y cantidad de datos enviados.

Este layout raíz presupone una fuente latina con licencia en app/fonts/body-latin.woff2 y el componente de medición que se muestra más adelante:

jsx
// app/layout.jsx
import localFont from 'next/font/local';
import { WebVitals } from './web-vitals';

const bodyFont = localFont({
  src: './fonts/body-latin.woff2',
  display: 'swap',
});

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={bodyFont.className}>
      <body>
        <WebVitals />
        {children}
      </body>
    </html>
  );
}

Es un layout en inglés, no una receta universal para fuentes multilingües. Las páginas en coreano y hebreo necesitan cobertura de caracteres, idioma y, cuando corresponda, dirección adecuados. Un archivo exclusivamente latino no puede representar todos los idiomas. Evita descargar varias familias completas en cada ruta cuando el visitante solo usa un idioma.

display: 'swap' permite mostrar texto con una fuente alternativa mientras llega la definitiva. Comprueba si el cambio altera los saltos de línea, el ancho de navegación o la altura de las tarjetas. Los ajustes de sustitución del framework ayudan, pero no demuestran que toda la página tenga CLS cero. Anuncios, contenido incrustado, banners insertados y secciones transmitidas también necesitan espacio reservado.

Compara la página con red lenta antes y después de la carga de la fuente. Observa el encabezado que determina LCP, no solo un párrafo de muestra. Si el texto se redistribuye, ajusta la fuente alternativa y el diseño o reduce esa dependencia. La guía de optimización de fuentes desarrolla este diagnóstico.

Programa los scripts sin confundir aplazamiento y eliminación

next/script controla cuándo se carga un script, pero no elimina el coste de ejecutarlo. Elige su alcance con tanto cuidado como la estrategia: un widget necesario en una ruta no debería ejecutarse automáticamente desde el layout raíz en todas ellas.

Las estrategias tienen funciones distintas:

Estrategia Momento previsto Precaución práctica
beforeInteractive Scripts críticos tempranos en el layout raíz Resérvala para código que toda la aplicación necesita pronto
afterInteractive Después de parte de la hidratación; predeterminada Puede competir con interacciones tempranas
lazyOnload Durante inactividad del navegador después de cargar La ejecución posterior aún puede interrumpir al visitante
worker Carga experimental en un worker La documentación revisada no la admite con App Router

Cargar más tarde puede proteger la ruta crítica inicial y empeorar una interacción posterior. Reproduce la acción que coincide con la inicialización del widget. Cuando proceda, carga una función no esencial solo al solicitarla el visitante y muestra una respuesta visible durante la espera. El consentimiento es independiente de la estrategia de carga: retrasar analítica no crea por sí mismo una integración que respete el consentimiento.

Investiga la hidratación y el trabajo posterior

La hidratación añade el comportamiento de React a una salida prerenderizada. Mantén coherentes la salida del servidor y la primera salida cliente. El estado exclusivo del navegador, los valores no deterministas o las ramas de contenido diferentes pueden provocar discrepancias. Ocultar todos los avisos no repara el modelo de entrega.

Una hidratación grande puede retrasar la entrada, pero no todo problema de INP procede de ella. Las actualizaciones posteriores pueden renderizar de nuevo subárboles extensos, provocar cálculos de diseño costosos o ejecutar operaciones síncronas. Graba la acción lenta tanto después de estabilizarse la página como durante la carga. Examina el intervalo entre la entrada del usuario y el siguiente pintado.

Las herramientas de planificación de React pueden priorizar algunas actualizaciones, pero no sacan automáticamente cualquier JavaScript síncrono del hilo principal. Elimina primero cálculos innecesarios, limita el subárbol actualizado y evita trabajo de diseño repetido. Para tareas intensivas de CPU, considera un worker adecuado. Comprueba siempre la corrección: un filtro rápido que muestra temporalmente un resultado incorrecto no es una optimización satisfactoria.

Mide producción con un callback estable

App Router utiliza useReportWebVitals de next/web-vitals dentro de un Client Component pequeño. Mantener estable la referencia del callback evita informes duplicados provocados por sustituir la función al renderizar de nuevo. Este ejemplo escribe observaciones en la consola y no transmite datos del usuario:

jsx
// app/web-vitals.jsx
'use client';

import { useReportWebVitals } from 'next/web-vitals';

function reportMetric({ id, name, value, rating }) {
  console.info('web-vital', { id, name, value, rating });
}

export function WebVitals() {
  useReportWebVitals(reportMetric);
  return null;
}

El layout anterior monta el componente una vez. Observa una compilación de producción, interactúa con la página y cambia su visibilidad al investigar métricas que informan más tarde en el ciclo de vida. No esperes que todas aparezcan inmediatamente tras cargar. Una visita sin interacciones válidas puede carecer de INP; no conviertas los valores ausentes en ceros.

Para recopilar datos reales, sustituye el registro por un sistema de telemetría diseñado explícitamente. Define muestreo, consentimiento cuando corresponda, minimización y eliminación de duplicados. Conserva el identificador y el nombre de la métrica y gestiona sus actualizaciones según la versión de la biblioteca. LCP e INP usan unidades de tiempo; CLS no tiene unidad. No promedies unidades distintas ni cuentes varias actualizaciones como visitas independientes.

Los cambios de ruta requieren atención adicional. La medición de navegaciones suaves depende del navegador y la biblioteca. No presupongas que cambiar la URL en cliente reinicia todas las Core Web Vitals ni asignes una métrica tardía a la URL visible cuando llega. Documenta las reglas de navegación de la versión desplegada.

Publica un cambio con un criterio claro de aceptación

Elige el cuello de botella, realiza el cambio pertinente más pequeño y repite el mismo recorrido en condiciones comparables. Para una imagen, comprueba solicitud, candidato seleccionado y LCP. Para un límite cliente, revisa inclusión en el paquete, respuesta y funcionalidad. Para un problema visual, observa la carga y las actualizaciones posteriores.

Usa el comprobador de Core Web Vitals para interpretar valores medidos y supervisa después las rutas y los grupos de dispositivos afectados. Mantén las guías de optimización por plataforma junto a la lista de publicación. El siguiente avance útil es una mejora registrada y vinculada a una causa, seguida de una comprobación de regresiones en el resto de la experiencia.

Guías relacionadas