Volver al Blog
WebDesarrolloSoftware

Optimización Avanzada de Core Web Vitals en Next.js 15: INP, LCP y CLS al 100%

Brayan Developer
5 min de lectura
Optimización Avanzada de Core Web Vitals en Next.js 15: INP, LCP y CLS al 100%
Guía práctica de ingeniería para alcanzar 100/100 en Google PageSpeed y Core Web Vitals con Next.js 15, optimizando Interaction to Next Paint (INP), LCP y estabilidad visual.

La velocidad de carga y la reactividad visual ya no son solo métricas técnicas: impactan directamente en el posicionamiento orgánico en Google (SEO), el coste de adquisición de clientes y las tasas de conversión en e-commerce. Con la introducción de Interaction to Next Paint (INP) como métrica oficial de Core Web Vitals reemplazando a FID, optimizar las aplicaciones web desarrolladas en Next.js 15 requiere técnicas avanzadas de renderizado en servidor, streaming de componentes y minimización del hilo principal (main thread).

Portada

Las 3 Métricas Críticas de Core Web Vitals#

+-------------------------------------------------------------------------------+
|                       MÉTRICAS CORE WEB VITALS OFICIALES                      |
+-------------------------------------------------------------------------------+
|                                                                               |
|  +------------------------+  +------------------------+  +-----------------+  |
|  |        1. LCP          |  |        2. INP          |  |     3. CLS      |  |
|  | Largest Contentful     |  | Interaction to Next    |  | Cumulative      |  |
|  | Paint (Velocidad)      |  | Paint (Reactividad)    |  | Layout Shift    |  |
|  +------------------------+  +------------------------+  +-----------------+  |
|  | • Objetivo: < 2.5s     |  | • Objetivo: < 200ms    |  | • Objetivo: <0.1|  |
|  | • Impacto: Portadas,   |  | • Impacto: Clics, taps,|  | • Impacto: Salto|  |
|  |   fuentes y banners    |  |   menús y modales      |  |   de elementos  |  |
|  +------------------------+  +------------------------+  +-----------------+  |
|                                                                               |
+-------------------------------------------------------------------------------+

1. Optimizando LCP (Largest Contentful Paint)#

El LCP mide el tiempo que tarda en visualizarse el elemento visual más grande de la pantalla (habitualmente la imagen de cabecera o el titular principal).

Reglas de oro para optimizar imágenes en Next.js:#

  1. Atributo priority exclusivo en el elemento principal: Evita que la imagen sea diferida (lazy loaded), forzando una precarga inmediata en el HTML.
  2. Atributo sizes explícito: Evita que el navegador descargue imágenes de 2000px en pantallas móviles de 390px.
  3. Formatos modernos (AVIF / WebP): Configurar Next.js para negociar compresión AVIF automática.
// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  images: {
    formats: ['image/avif', 'image/webp'],
    minimumCacheTTL: 31536000, // 1 año de caché para assets inmutables
    deviceSizes: [640, 750, 828, 1080, 1200, 1920],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  },
};

export default nextConfig;

Componente de Portada Optimizado#

import Image from 'next/image';

export function HeroCover({ src, alt }: { src: string; alt: string }) {
  return (
    <div className="relative w-full aspect-[16/9] overflow-hidden rounded-2xl bg-slate-900">
      <Image
        src={src}
        alt={alt}
        fill
        priority
        sizes="(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px"
        className="object-cover"
      />
    </div>
  );
}

2. Dominando INP (Interaction to Next Paint)#

El INP mide la latencia de todas las interacciones de un usuario a lo largo de su visita. Ocurre cuando tareas pesadas de JavaScript bloquean el hilo principal e impiden que el navegador actualice la pantalla tras un clic.

Estrategia 1: useTransition para operaciones no bloqueantes#

Al filtrar listados grandes o realizar búsquedas en vivo, envolver la actualización del estado en startTransition permite que el navegador atienda clics del usuario sin congelarse:

'use client';

import { useState, useTransition } from 'react';

export function LiveFilter({ items }: { items: string[] }) {
  const [query, setQuery] = useState('');
  const [filteredList, setFilteredList] = useState(items);
  const [isPending, startTransition] = useTransition();

  const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    setQuery(value); // Actualización urgente e inmediata del input

    startTransition(() => {
      // Actualización diferida de baja prioridad (no bloquea el hilo principal)
      const results = items.filter((item) =>
        item.toLowerCase().includes(value.toLowerCase())
      );
      setFilteredList(results);
    });
  };

  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={handleSearch}
        placeholder="Buscar..."
        className="px-4 py-2 bg-slate-800 rounded-lg text-white"
      />
      {isPending && <span className="text-xs text-slate-400 ml-2">Filtrando...</span>}
      <ul className="mt-4">
        {filteredList.map((item) => (
          <li key={item} className="py-1 text-slate-300">{item}</li>
        ))}
      </ul>
    </div>
  );
}

Estrategia 2: Carga Dinámica (Lazy Import) de Componentes Pesados#

Modales complejos, gráficos interactivos o editores de texto no deben cargarse en el bundle inicial:

import dynamic from 'next/dynamic';

const DynamicChartModal = dynamic(
  () => import('@/components/HeavyAnalyticsChart'),
  {
    loading: () => <div className="h-64 bg-slate-800 animate-pulse rounded-xl" />,
    ssr: false, // Solo se inicializa en el cliente cuando el usuario abre el modal
  }
);

3. Eliminando el CLS (Cumulative Layout Shift)#

El CLS evalúa la estabilidad visual. Ocurre cuando elementos sin dimensiones fijas (banners publicitarios, imágenes sin ratio o fuentes web que parpadean) empujan el contenido mientras el usuario lee.

Soluciones Arquitectónicas:#

  1. Fuentes con next/font: Descarga las fuentes en tiempo de compilación y calcula el tamaño de reemplazo (fallback adjust) para erradicar el parpadeo tipográfico (FOIT/FOUT).
  2. Contenedores con Aspect Ratio Fijo: Utilizar clases como aspect-[16/9] o min-h-[300px] en contenedores dinámicos para reservar el espacio antes de que el contenido cargue.
  3. Skeletons en Streaming con React Suspense:
import { Suspense } from 'react';
import { PostComments } from './PostComments';
import { CommentsSkeleton } from './CommentsSkeleton';

export default function BlogPostPage() {
  return (
    <article className="max-w-4xl mx-auto py-10">
      <h1>Título del Artículo</h1>
      <p>Contenido del post...</p>

      {/* El skeleton reserva el espacio exacto, eliminando CLS */}
      <Suspense fallback={<CommentsSkeleton />}>
        <PostComments />
      </Suspense>
    </article>
  );
}

Resultados y Puntuaciones en Producción#

Indicador Antes de Optimizar Con Next.js 15 Optimizado Estado Google
LCP (Largest Contentful Paint) 3.8s (Malo) 0.7s 🟢 Bueno
INP (Interaction to Next Paint) 320ms (Malo) 38ms 🟢 Bueno
CLS (Cumulative Layout Shift) 0.28 (Malo) 0.002 🟢 Bueno
Puntuación Google Lighthouse 64 / 100 100 / 100 🏆 Excelente

Conclusión#

Alcanzar la puntuación máxima en Core Web Vitals en Next.js 15 requiere una combinación metódica de Server Components, Streaming con Suspense, optimización rigurosa de imágenes/fuentes y el uso de useTransition para liberar el hilo principal.

¿Necesitas realizar una auditoría de rendimiento web y optimizar la velocidad y SEO técnico de tu sitio o tienda online? Consulta nuestros Servicios de Desarrollo Web o solicita una auditoría con Brayan Developer.

Etiquetas
Next.jsCore Web VitalsRendimiento WebSEO TécnicoINPLCP
Compartir:XLinkedInWhatsApp

¿Te gustaría profundizar en estos temas?

Aprende sobre desarrollo de software, apps a medida, automatizaciones con N8N, Next.js y Cloud con casos reales.

Hablemos