Next.js Core Web Vitals en la práctica: guía completa de optimización LCP/FCP/CLS

El mes pasado, me hice cargo de la optimización del rendimiento de un proyecto de comercio electrónico: la puntuación de Lighthouse se estancó en torno a 65, el LCP (Largest Contentful Paint) rondaba los 3,5 segundos. La tasa de abandono era alta y la conversión no despegaba.
Dos semanas de optimización sistemática de Core Web Vitals después: Lighthouse a 95, LCP a 1,2 s. Y sobre todo, +28% de conversión. La optimización del rendimiento no se trata sólo de métricas: se trata de un valor empresarial concreto.
Según datos de 2025, sólo el 47% de los sitios cumplen con el estándar Core Web Vitals de Google. Un rendimiento deficiente puede costar entre un 8 % y un 35 % en ingresos, clasificaciones y conversiones. Esto no es alarmismo: es el precio real de los negocios.
En este artículo, comparto mi experiencia de campo sobre la optimización de Next.js. Allí encontrarás:
- Métodos concretos para optimizar las 3 métricas clave (LCP, INP, CLS)
- Más de 10 ejemplos de código listos para copiar
- 5 errores de rendimiento más comunes
Core Web Vitals: qué cambiará en 2025
Antes de optimizar, aclaremos las reglas del juego. Muchos artículos todavía hablan de FID (First Input Delay), aunque esta métrica se eliminó en marzo de 2024.
Las tres métricas clave actuales
Google ahora se centra en estos tres indicadores:
-
LCP (Largest Contentful Paint) — pintura del contenido principal
- Objetivo: ≤ 2,5 s
- Mide el rendimiento de carga
- Pesa el 25% en la puntuación Lighthouse Performance
-
INP (Interacción con la siguiente pintura) — interacción hasta el siguiente renderizado
- Objetivo: ≤ 200 ms
- Mide la capacidad de respuesta a las interacciones.
- Nueva métrica desde marzo de 2024, reemplaza FID
-
CLS (Cambio de diseño acumulativo): cambio de diseño acumulativo
- Objetivo: < 0,1
- Mide la estabilidad visual
FCP (First Contentful Paint) ya no es una métrica de Core Web Vitals, pero sigue siendo importante para las primeras impresiones de los usuarios.
¿Por qué estar interesado?
Probablemente conozcas este escenario: abres un sitio, apuntas a un botón, la página salta y haces clic en otro lugar. Es un CLS malo.
O esperas varios segundos frente a una pantalla blanca preguntándote si se corta la conexión. Es un LCP demasiado lento.
Según el equipo de Chrome, el LCP promedio de los mejores sitios es de alrededor de 1220 ms. Si su LCP supera los 2,5 segundos, está detrás de la mayoría de sus competidores.
Optimización LCP: muestra rápidamente el elemento más grande
LCP ha sido la métrica en la que más he trabajado y en la que las ganancias son más visibles. He aquí cómo hacerlo.
Paso 1: Identifique su elemento LCP
Antes de optimizar, necesita saber qué elemento es el LCP. En general:
- La imagen del héroe en la mitad superior de la página.
- La miniatura de un vídeo.
- Un bloque de título o texto grande.
Método rápido:
- Abra Chrome DevTools (F12)
Ctrl+Mayús+P(Mac:Cmd+Mayús+P)- Escriba “Mostrar renderizado”
- Marque “Ventajas web principales”
- Actualizar: el elemento LCP se muestra en la parte superior derecha
En el proyecto de comercio electrónico, el elemento LCP era la imagen principal de la página de inicio: un JPG de 2 MB. Ahí estaba el problema.
Optimización de imagen: use siguiente/imagen correctamente
Muchos piensan que usar el componente Imagen de Next.js es suficiente. Este no es el caso: cometí este error al principio y el LCP siguió siendo lento.
La prioridad es clave
Para la imagen LCP, debes decirle explícitamente al navegador: “¡Esta imagen es importante, cárgala como prioridad!”. » Next.js ofrece dos enfoques:
Next.js 13-15 (versiones actuales):
import Image from 'next/image';
export default function Hero() {
return (
<Image
src="/hero-image.jpg"
width={1200}
height={630}
priority // ¡Clave! Prioridad de carga
fetchPriority="high" // Doble seguridad
alt="Imagen principal del producto"
/>
);
}
Next.js 16+ (última versión):
Next.js 16 desaprobó “prioridad” a favor de:
<Image
src="/hero-image.jpg"
width={1200}
height={630}
loading="eager" // Carga inmediata, sin lazy load
fetchPriority="high" // Alta prioridad
alt="Imagen principal del producto"
/>
¿Por qué dos atributos? prioridad agrega automáticamente una etiqueta de precarga; fetchPriority indica la importancia del recurso. Juntos funcionan mejor.
Para comparar con malas prácticas:
// ❌ Error: lazy load por defecto, LCP lento
<Image
src="/hero-image.jpg"
width={1200}
height={630}
alt="Imagen principal del producto"
/>
// ❌ Error: faltan dimensiones
<Image
src="/hero-image.jpg"
priority
alt="Imagen principal del producto"
/>
La importancia de las dimensiones
El componente “Imagen” requiere “ancho” y “alto”; esto no es una restricción arbitraria, sino una protección contra CLS.
Para un diseño responsivo:
// Utiliser fill pour le responsive
<div style={{ position: 'relative', width: '100%', aspectRatio: '16/9' }}>
<Image
src="/hero-image.jpg"
fill
priority
style={{ objectFit: 'cover' }}
alt="Imagen principal del producto"
/>
</div>
El contenedor principal debe tener un tamaño o “relación de aspecto” explícito; de lo contrario, el navegador no sabe qué área asignar.
Optimización de fuentes: siguiente/fuente en línea automática
La carga lenta de fuentes también penaliza al LCP, especialmente las fuentes CJK que pesan varios MB. Next.js 13 introdujo next/font para automatizar la optimización.
Fuentes de Google:
// app/layout.jsx
import { Inter, Noto_Sans_SC } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap', // Evita el parpadeo de texto
});
const notoSansSC = Noto_Sans_SC({
subsets: ['chinese-simplified'],
weight: ['400', '700'],
display: 'swap',
});
export default function RootLayout({ children }) {
return (
<html lang="zh-CN" className={`${inter.className} ${notoSansSC.className}`}>
<body>{children}</body>
</html>
);
}
Fuentes personalizadas:
import localFont from 'next/font/local';
const myFont = localFont({
src: './my-font.woff2',
display: 'swap',
});
export default function Layout({ children }) {
return <div className={myFont.className}>{children}</div>;
}
next/font optimiza automáticamente:
- CSS de fuente en línea, menos solicitudes de red
- Subconjunto de personajes, solo carga lo necesario
- Precargar archivos de fuentes
- Se eliminaron las compensaciones de diseño.
Después de migrar a next/font, el LCP cayó otros 0,3 s.
Reducir el tiempo de respuesta del servidor
¿Imágenes y fuentes optimizadas, pero el LCP sigue siendo lento? TTFB (tiempo hasta el primer byte) puede ser la causa.
Favorecer la generación estática
Next.js ofrece varios modos de renderizado, del más rápido al más lento:
- SSG (Generación de sitios estáticos): HTML generado durante la construcción, el más rápido
- ISR (Regeneración estática incremental): regeneración estática según demanda
- SSR (Representación del lado del servidor): HTML generado en cada solicitud, más lento
- CSR (Representación del lado del cliente): renderización del lado del cliente, LCP más lento
Utilice SSG siempre que sea posible:
// app/blog/[slug]/page.jsx
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map((post) => ({
slug: post.slug,
}));
}
export default async function BlogPost({ params }) {
const post = await getPost(params.slug);
return <article>{/* ... */}</article>;
}
Si los datos deben permanecer actualizados, utilice ISR:
// app/products/[id]/page.jsx
export const revalidate = 3600; // Régénération toutes les heures
export default async function ProductPage({ params }) {
const product = await getProduct(params.id);
return <div>{/* ... */}</div>;
}
CDN y red perimetral
En Vercel, los recursos estáticos se distribuyen a través de la red perimetral global, lo que reduce en gran medida el TTFB.
En otras plataformas, combínelo con Cloudflare, AWS CloudFront o CDN equivalente.
Evite cálculos pesados del lado del servidor
También caí en esta trampa: el procesamiento de datos complejos en el lado del servidor tomó 500 ms para cada renderizado, agotando el LCP.
Contraejemplo:
// ❌ Error: cálculo en cada solicitud
export default async function Page() {
const data = await fetchData();
const processed = heavyProcessing(data); // 500 ms
return <div>{processed}</div>;
}
Buen enfoque:
Almacene en caché el resultado o calcule en la compilación:
// ✅ Correcto: cálculo en build
export async function generateStaticParams() {
const data = await fetchData();
const processed = heavyProcessing(data);
await saveProcessedData(processed);
}
export default async function Page() {
const processed = await getProcessedData(); // Lecture directe
return <div>{processed}</div>;
}
Optimización de CLS: elimine las interrupciones del diseño
CLS (Cambio de diseño acumulativo) es la métrica más frustrante. La página salta de repente: mala experiencia. Este problema me mantuvo ocupado por un tiempo.
Reservar dimensiones de imagen y medios
La causa más común: una imagen que se carga y cambia el diseño. Solución: indicar las dimensiones con antelación.
El componente Image Next.js se encarga de esto:
// ✅ Correcto: dimensiones explícitas, sin CLS
<Image
src="/product.jpg"
width={400}
height={300}
alt="Imagen del producto"
/>
¿Y para imágenes dinámicas?
Desde un CMS, sin saber las dimensiones:
// ✅ Correcto: aspect-ratio para reservar espacio
<div style={{ position: 'relative', width: '100%', aspectRatio: '4/3' }}>
<Image
src={dynamicImageUrl}
fill
style={{ objectFit: 'cover' }}
alt="Imagen dinámica"
/>
</div>
La misma lógica para el vídeo:
<video
width="1280"
height="720"
poster="/video-poster.jpg"
controls
>
<source src="/video.mp4" type="video/mp4" />
</video>
Marcador de posición para contenido dinámico
Los anuncios, los banners de notificación, las incrustaciones (tarjetas de Twitter, etc.) provocan CLS. Reserva el espacio con antelación.
Contenedor publicitario:
// ✅ Correcto: altura reservada
<div
style={{
minHeight: '250px', // Hauteur standard Google AdSense
backgroundColor: '#f0f0f0' // Couleur de placeholder
}}
>
{/* Script de anuncios */}
<ins className="adsbygoogle" />
</div>
Banner de notificación:
No dejes que de repente aparezca una venda en los ojos:
// ❌ Error: aparece de repente y empuja la página
{showBanner && <NotificationBanner />}
// ✅ Correcto: espacio reservado
<div style={{ minHeight: '60px' }}>
{showBanner ? <NotificationBanner /> : <div style={{ height: '60px' }} />}
</div>
Pantallas de esqueleto:
Para contenido cargado dinámicamente, utilice esqueletos:
function ProductList() {
const { data, isLoading } = useQuery('products', fetchProducts);
if (isLoading) {
return (
<div className="grid grid-cols-3 gap-4">
{Array.from({ length: 6 }).map((_, i) => (
<div key={i} className="skeleton" style={{ height: '300px' }} />
))}
</div>
);
}
return (
<div className="grid grid-cols-3 gap-4">
{data.map(product => <ProductCard key={product.id} {...product} />)}
</div>
);
}
Optimización de carga de fuentes
La carga de fuentes también provoca CLS: cambio de fuente visible o parpadeante.
next/font maneja esto automáticamente, pero puedes ajustarlo:
const inter = Inter({
subsets: ['latin'],
display: 'optional', // Si la fuente no carga a tiempo, usar la del sistema
adjustFontFallback: true, // Ajusta el fallback para limitar el salto
});
Opciones para mostrar:
swap: primero retrocede, luego cambia (puede causar CLS)opcional: si la fuente no está lista a tiempo, conserve la fuente alternativa (recomendado)block: texto oculto brevemente mientras se espera la fuente (no recomendado)fallback: compromiso entre swap y opcional
Recomiendo “opcional”: es posible que la fuente personalizada no se muestre, pero la experiencia permanece estable.
Evite el uso de JavaScript responsivo
¡El error más común de CLS en 2025! Muchos proyectos de Next.js cometen este error.
Código normalmente problemático:
// ❌ Error: CLS grave
import { useMediaQuery } from '@/hooks/useMediaQuery';
export default function ResponsiveLayout() {
const isMobile = useMediaQuery('(max-width: 768px)');
return (
<div>
{isMobile ? (
<MobileNav />
) : (
<DesktopNav />
)}
</div>
);
}
¿Por qué CLS?
- En la primera representación, JavaScript aún no se ha ejecutado:
isMobileesindefinidoo es un valor predeterminado - Después de la ejecución,
useMediaQuerydevuelve el valor verdadero - Volver a renderizar, diseño que cambia abruptamente
En SSR, es peor: el servidor no conoce el ancho de la pantalla del cliente.
Buen enfoque: consultas de medios CSS
// ✅ Correcto: mostrar/ocultar con CSS, sin CLS
export default function ResponsiveLayout() {
return (
<>
<nav className="mobile-nav md:hidden">
<MobileNav />
</nav>
<nav className="desktop-nav hidden md:block">
<DesktopNav />
</nav>
</>
);
}
O con CSS-en-JS:
export default function ResponsiveLayout() {
return (
<div className="responsive-container">
<MobileNav />
<DesktopNav />
<style jsx>{`
.responsive-container > :global(.mobile-nav) {
display: block;
}
.responsive-container > :global(.desktop-nav) {
display: none;
}
@media (min-width: 768px) {
.responsive-container > :global(.mobile-nav) {
display: none;
}
.responsive-container > :global(.desktop-nav) {
display: block;
}
}
`}</style>
</div>
);
}
Si realmente necesita distinguir en el lado JS (diferentes solicitudes según el dispositivo), obtenga el User-Agent en el lado del servidor:
// app/page.jsx
import { headers } from 'next/headers';
export default async function Page() {
const headersList = headers();
const userAgent = headersList.get('user-agent') || '';
const isMobile = /mobile/i.test(userAgent);
return (
<div>
{isMobile ? <MobileView /> : <DesktopView />}
</div>
);
}
El servidor y el cliente representan lo mismo: sin CLS.
Optimización FCP: acelera el primer contenido visible
FCP no es una métrica de Core Web Vitals, pero da forma a la primera impresión. Una pantalla en blanco demasiado larga y el usuario cierra la pestaña.
Optimizar los recursos críticos
El FCP lento a menudo proviene de recursos que bloquean el procesamiento. Abra el panel Cobertura de Chrome DevTools para detectar CSS y JavaScript no utilizados.
CSS crítico en línea:
Next.js optimiza CSS automáticamente, pero puedes incorporar estilos esenciales:
// app/layout.jsx
export default function RootLayout({ children }) {
return (
<html>
<head>
<style
dangerouslySetInnerHTML={{
__html: `
/* CSS crítico: estilos indispensables del primer pantallazo */
body { margin: 0; font-family: system-ui; }
.hero { height: 100vh; }
`,
}}
/>
</head>
<body>{children}</body>
</html>
);
}
Aplazar CSS no crítico:
Para estilos que no son de primera pantalla (secciones modales y plegables):
// Import dynamique du CSS dans le composant
import dynamic from 'next/dynamic';
const Modal = dynamic(() => import('./Modal'), {
loading: () => <p>Loading...</p>,
});
Gestión de scripts de terceros
Google Analytics, anuncios, complementos sociales: tantos factores que afectan el rendimiento. El FCP de muchos sitios sufre esto.
Usar siguiente/script:
Next.js proporciona el componente Script para controlar el tiempo de carga:
import Script from 'next/script';
export default function Layout({ children }) {
return (
<>
{children}
{/* Google Analytics : après interactivité */}
<Script
src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
strategy="lazyOnload"
/>
<Script id="ga-init" strategy="lazyOnload">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'GA_MEASUREMENT_ID');
`}
</Script>
{/* Facebook Pixel : chargement différé */}
<Script
src="https://connect.facebook.net/en_US/fbevents.js"
strategy="lazyOnload"
/>
</>
);
}
valores de estrategia:
beforeInteractive: antes de la interactividad (scripts críticos)afterInteractive: después de la interactividad (predeterminado)lazyOnload: en inactivo (recomendado para análisis y anuncios)worker: en un Web Worker (experimental)
Al cambiar todos los scripts no esenciales a “lazyOnload”, el FCP ganó 0,8 s.
División de código y carga diferida
Next.js divide el código automáticamente, pero el refinamiento manual sigue siendo útil.
Componentes de carga diferida:
Para lo que no se ve a la primera pintura:
import dynamic from 'next/dynamic';
// Lazy load du composant commentaires
const Comments = dynamic(() => import('./Comments'), {
loading: () => <div>Cargando comentarios...</div>,
ssr: false, // Sin renderizado en servidor
});
export default function BlogPost({ post }) {
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
{/* Chargé quand l'utilisateur scroll jusqu'ici */}
<Comments postId={post.id} />
</article>
);
}
Carga diferida de bibliotecas de terceros:
Algunas bibliotecas son pesadas (gráficos, editores enriquecidos): cárguelas a pedido.
Caso: Chart.js: 800 KB guardados
Un proyecto importó Chart.js en cada página mientras que solo el panel lo usaba. 800 KB de JS inútiles por todas partes.
Antes de la optimización:
// ❌ Error: import global, todas las páginas lo cargan
import { Chart } from 'chart.js';
export default function Dashboard() {
return <canvas ref={chartRef} />;
}
Después de la optimización:
// ✅ Correcto: carga bajo demanda
import dynamic from 'next/dynamic';
const ChartComponent = dynamic(() => import('./ChartComponent'), {
loading: () => <div>Cargando gráfico...</div>,
ssr: false,
});
export default function Dashboard() {
return <ChartComponent />;
}
// ChartComponent.jsx
import { Chart } from 'chart.js';
export default function ChartComponent() {
return <canvas ref={chartRef} />;
}
Solo los visitantes del panel descargan Chart.js.
Carga diferida de imágenes:
Excluyendo la imagen LCP, las otras imágenes deben cargarse de forma diferida:
<Image
src="/feature-image.jpg"
width={600}
height={400}
loading="lazy" // Valor por defecto, se puede omitir
alt="Presentación de funciones"
/>
##Monitoreo y optimización continuos
La optimización no es algo que se haga de una sola vez. El rendimiento se mantiene en el tiempo.
Lighthouse CI automáticamente
Lanzar Lighthouse a mano lleva tiempo y lo olvidas. Es mejor integrarlo con CI/CD.
Instalación:
npm install -D @lhci/cli
Configuración lighthouserc.js:
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000/', 'http://localhost:3000/products'],
numberOfRuns: 3, // 3 ejecuciones, promedio
},
assert: {
assertions: {
'categories:performance': ['error', { minScore: 0.9 }], // Performance ≥ 90
'first-contentful-paint': ['error', { maxNumericValue: 2000 }], // FCP ≤ 2s
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }], // LCP ≤ 2,5s
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }], // CLS < 0,1
},
},
upload: {
target: 'temporary-public-storage',
},
},
};
Acciones de GitHub:
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm install
- run: npm run build
- run: npm run start &
- run: npx @lhci/cli autorun
Cada pulsación activa Lighthouse. Una regresión del rendimiento hace que el CI falle.
Monitoreo de usuarios reales (RUM)
Medidas faro en laboratorio: la experiencia real puede diferir. Recopile Core Web Vitals del campo.
Vercel Analytics:
En Vercel, activación con un clic:
// app/layout.jsx
import { Analytics } from '@vercel/analytics/react';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
);
}
biblioteca web-vitals:
Sin Vercel, utilice la biblioteca web-vitals de Google:
npm install web-vitals
// app/layout.jsx
'use client';
import { useEffect } from 'react';
import { onLCP, onFID, onCLS, onINP } from 'web-vitals';
function sendToAnalytics(metric) {
fetch('/api/analytics', {
method: 'POST',
body: JSON.stringify(metric),
});
}
export default function Analytics() {
useEffect(() => {
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
}, []);
return null;
}
Consola de búsqueda de Google:
El informe “Señales web esenciales” muestra el rendimiento del campo y marca las páginas para mejorar.
Errores y soluciones comunes
Finalmente, los 5 errores más comunes:
Error 1: Demasiado JavaScript del lado del cliente
Muchos desarrolladores hacen todo en React: la página depende completamente de JS. Si la carga falla o se retrasa, pantalla blanca.
Soluciones:
- Favorecer los componentes del servidor (Next.js 13+ predeterminado)
'usar cliente'sólo si es necesario- Mejora progresiva: funcionalidad básica primero, enriquecimiento después
Trampa 2: ignorar el rendimiento móvil
Rápido en computadoras de escritorio no significa rápido en dispositivos móviles. CPU débil, red lenta: los problemas van en aumento.
Soluciones:
- Prueba en modo “Móvil” en Lighthouse
- Limitación de red (Chrome DevTools → Red)
- Aceleración de la CPU (Chrome DevTools → Rendimiento → CPU)
Error 3: scripts de terceros no optimizados
Google Analytics, Facebook Pixel, Intercom, Hotjar… cada script hace que la página sea más pesada.
Soluciones:
- Ajustar con
next/script,strategy="lazyOnload" - Auditoría periódica: ¿qué guiones son realmente necesarios?
- Seguimiento del servidor en lugar del cliente cuando sea posible
Trampa 4: elección incorrecta del formato de imagen
¿Aún en PNG y JPG? WebP y AVIF ahorran entre un 30 y un 50 % de volumen.
Soluciones:
El componente Image Next.js se convierte automáticamente; asegúrese de que el servidor lo admita:
// next.config.js
module.exports = {
images: {
formats: ['image/avif', 'image/webp'], // Formatos modernos prioritarios
},
};
Trampa 5: descuidar el rendimiento del servidor
Una interfaz perfecta no compensa un servidor lento.
Soluciones:
- Optimizar consultas BDD (indexar, evitar N+1)
- Caché (Redis, CDN)
- Monitoreo de servidores (APM: New Relic, Datadog)
Resumen
Recapitulemos:
Puntos clave del LCP:
priorityyfetchPriority="high"en la imagen LCPsiguiente/fuentepara fuentes- SSG e ISR como prioridad, menos cálculo del servidor
- CDN para reducir TTFB
Puntos clave de CLS:
- Dimensiones explícitas para imágenes y medios.
- Espacio reservado para contenido dinámico (anuncios, banners)
- No hay diseño responsivo a través de enlaces de JavaScript
next/fontpara limitar los saltos de fuente
Puntos clave del FCP:
- Aplazar recursos no críticos
- Scripts de terceros a través de
next/scriptconlazyOnload - Carga diferida de grandes bibliotecas y componentes excluyendo la primera pantalla
Optimización continua:
- CI de faro automatizado
- Datos reales del usuario (RUM)
- Auditoría y limpieza periódicas.
La optimización del rendimiento sigue un ciclo de “medir → optimizar → volver a medir”. No hay una solución única: atención y mejora continuas.
Cuando la puntuación de Lighthouse finalmente superó los 90, la satisfacción fue real. Y el aumento de las conversiones demostró que el esfuerzo valió la pena.
Plan de acción
Depende de ti. Recomiendo este orden:
-
Inmediato:
- Pruebe su proyecto Next.js con Lighthouse
- Identificar el elemento LCP: ¿se ha establecido una “prioridad”?
- Busque
useMediaQueryo código similar que cause CLS
-
Esta semana:
- Optimizar imagen LCP (prioridad + fetchPriority)
- Migrar fuentes a
siguiente/fuente - Agregar marcadores de posición para contenido dinámico
-
La próxima semana:
- Scripts de terceros en
lazyOnload - Carga diferida de grandes bibliotecas y componentes excluyendo la primera pantalla
- Configurar Lighthouse CI
- Scripts de terceros en
-
Continuo:
- Verifique la puntuación de Lighthouse todos los meses
- Realice un seguimiento del informe Core Web Vitals en Search Console
- Regresiones rápidamente correctas.
La optimización del rendimiento no es un proyecto único: es un proceso continuo. ¡Feliz optimización!
Flujo completo de optimización de Core Web Vitals en Next.js
Optimizar LCP, INP y CLS para subir la puntuación de Lighthouse por encima de 90
⏱️ Estimated time: 8 hr
- 1
Step 1: Optimizar LCP (Largest Contentful Paint)
Objetivo: ≤2,5 s
Métodos:
• Usar next/image para optimizar imágenes (conversión automática a WebP/AVIF)
• Añadir priority a imágenes críticas del primer pantallazo
• Precargar recursos clave (con <link rel="preload">)
• Optimizar tiempo de respuesta del servidor (CDN, Edge Functions)
• Reducir recursos que bloquean el renderizado (carga diferida de CSS/JS no crítico)
• Usar React Server Components para reducir JS en el cliente
Herramientas:
• Informe Performance de Lighthouse
• Panel Performance de Chrome DevTools
• Pruebas online con WebPageTest - 2
Step 2: Optimizar INP (Interaction to Next Paint)
Objetivo: ≤200 ms
Métodos:
• Reducir tiempo de ejecución de JavaScript (code splitting, lazy loading)
• Usar React Server Components para reducir JS en el cliente
• Optimizar manejo de eventos (debounce, throttle, delegación)
• Evitar tareas largas que bloqueen el hilo principal (Web Workers)
• Optimizar scripts de terceros (carga diferida, async/defer)
• Usar Suspense y renderizado en streaming
Herramientas:
• Panel Performance de Chrome DevTools
• Extensión Web Vitals para Chrome
• Herramientas de Real User Monitoring (RUM) - 3
Step 3: Optimizar CLS (Cumulative Layout Shift)
Objetivo: ≤0,1
Métodos:
• Definir ancho y alto de imágenes y vídeos (o usar aspect-ratio)
• Evitar insertar contenido dinámico (reservar espacio)
• Usar font-display: swap para evitar desplazamientos por fuentes
• Reservar espacio para anuncios (evitar que empujen el contenido al cargar)
• Usar CSS Grid/Flexbox en lugar de posicionamiento absoluto
• Evitar insertar contenido nuevo encima del existente
Herramientas:
• Informe CLS de Lighthouse
• Registro Layout Shift en el panel Performance de Chrome DevTools
• Visualización CLS en WebPageTest - 4
Step 4: Usar las funciones de optimización de Next.js
Trucos de Next.js:
• Componente Image para optimizar imágenes automáticamente
• Font Optimization para optimizar fuentes
• React Server Components para reducir JS en el cliente
• Streaming SSR para acelerar el primer pantallazo
• Dynamic Imports para code splitting
• next/dynamic para cargar componentes de forma diferida
Comprobaciones:
• Configuración de rendimiento en next.config.js
• Revisar tamaño del bundle (npm run build) - 5
Step 5: Monitorizar y optimizar de forma continua
Herramientas de monitorización:
• Informe Core Web Vitals de Google Search Console
• Vercel Analytics (si usas Vercel)
• Extensión Web Vitals para Chrome
• Herramientas de Real User Monitoring (RUM)
Optimización continua:
• Revisar la puntuación de Lighthouse cada mes
• Seguir el informe Core Web Vitals de Search Console
• Corregir regresiones de rendimiento a tiempo
• Probar el efecto de las optimizaciones con A/B testing
FAQ
¿Cuáles son las tres métricas de Core Web Vitals?
¿Cómo optimizar LCP?
¿Cuál es la diferencia entre INP y FID?
¿Cómo evitar CLS (desplazamiento de diseño)?
¿Cuánto tarda en verse el efecto tras optimizar el rendimiento?
¿Qué funciones de Next.js ayudan a optimizar el rendimiento?
¿Cómo monitorizar Core Web Vitals?
16 min de lectura · Publicado el: 19 dic 2025 · Actualizado el: 21 ago 2026
Guía completa de Next.js
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Guía completa de optimización de imágenes en Next.js: uso correcto del componente Image
Guía completa del componente Image de Next.js: soluciona carga lenta, errores de configuración de imágenes remotas y desplazamiento de layout. Incluye novedades de Next.js 14/15, ejemplos de código y técnicas de optimización para mejorar el rendimiento un 60-80 %.
Parte 26 de 51
Siguiente
Guía completa de SEO en Next.js: Metadata API y datos estructurados en la práctica
Guía completa para configurar SEO en Next.js 15 con Metadata API: datos estructurados, Open Graph y Twitter Cards con código de ejemplo y consejos para evitar errores comunes y multiplicar el tráfico
Parte 28 de 51



Comentarios
Inicia sesión con GitHub para dejar un comentario