Optimización de Imágenes en Next.js para Core Web Vitals en 2026

He pasado los últimos cuatro años optimizando imágenes en aplicaciones Next.js, y siendo honesto -- el panorama en 2026 no se parece en nada a cómo era cuando next/image se lanzó por primera vez. Los umbrales de Core Web Vitals de Google se han endurecido, nuevos formatos de imagen han madurado, y el componente next/image en sí ha pasado por múltiples reescrituras. Si tus imágenes siguen configuradas como lo estaban en 2023, estás dejando rendimiento (y rankings) sobre la mesa.

Esto no es un resumen de la documentación de Next.js. Es lo que he aprendido al enviar docenas de sitios de producción donde las puntuaciones de LCP realmente importan -- donde una diferencia de 200ms en la carga de imágenes significaba la diferencia entre la página uno y la página tres.

Tabla de Contenidos

Next.js Image Optimization for Core Web Vitals in 2026

Core Web Vitals en 2026: Qué Cambió

Google actualizó los umbrales de Core Web Vitals a fines de 2025, y los cambios no fueron triviales. Aquí es donde están las cosas:

Métrica Umbral "Bueno" 2023 Umbral "Bueno" 2026 Qué Mide
LCP ≤ 2.5s ≤ 2.0s Largest Contentful Paint
INP ≤ 200ms ≤ 150ms Interaction to Next Paint
CLS ≤ 0.1 ≤ 0.1 Cumulative Layout Shift
TTFB N/A (no es CWV) Informalmente ≤ 600ms Time to First Byte

La caída del umbral de LCP de 2.5s a 2.0s fue la que más afectó a los sitios con muchas imágenes. Medio segundo no suena como mucho hasta que te das cuenta de que en el 60%+ de las páginas, el elemento LCP es una imagen. Usualmente una imagen hero, una foto de producto, o una miniatura de artículo destacado.

INP reemplazando a FID ya estaba en vigor, pero el umbral apretado de 150ms significa que los paquetes JavaScript pesados -- incluyendo scripts de lazy-loading de imágenes mal configurados -- pueden hundir tus puntuaciones de interactividad.

CLS se mantuvo igual, lo cual es buena noticia. Pero las imágenes siguen siendo la causa número uno de cambio de layout cuando no tienen dimensiones explícitas.

Por Qué Esto Importa para Next.js Específicamente

Las aplicaciones Next.js tienden a ser pesadas en JavaScript por naturaleza. Estás enviando React, estás enviando código del framework, y si no tienes cuidado, estás enviando lógica de optimización de imágenes del lado del cliente también. La combinación de un presupuesto de LCP más estricto y la sobrecarga de JS de una aplicación React significa que tienes menos margen de error que un sitio HTML estático.

Este es exactamente el motivo por el que nos enfocamos mucho en desarrollo Next.js -- el framework te proporciona herramientas increíbles, pero solo si sabes cómo configurarlas.

Cómo Funciona next/image Realmente Bajo el Capó

Desmitifiquemos qué sucede cuando usas <Image /> de next/image. Entender el pipeline te ayuda a tomar mejores decisiones de optimización.

El Flujo de Solicitud

  1. Tiempo de compilación: Next.js genera HTML con una etiqueta <img> que apunta a /_next/image?url=...&w=...&q=...
  2. Primera solicitud: La API de optimización de imágenes de Next.js recibe la solicitud, obtiene la imagen original, la redimensiona, convierte el formato, y almacena el resultado en caché
  3. Solicitudes posteriores: La versión en caché se sirve directamente

En Next.js 15 (el estable actual a partir de principios de 2026), el optimizador de imágenes usa sharp por defecto en entornos Node.js. En Vercel, usa su servicio de optimización de imágenes basado en edge. En otras plataformas, regresa a sharp o squoosh dependiendo de tu configuración.

// Uso básico -- pero hay mucho sucediendo detrás de esto
import Image from 'next/image';

export default function Hero() {
  return (
    <Image
      src="/hero.jpg"
      alt="Product hero shot"
      width={1200}
      height={630}
      priority
      quality={80}
    />
  );
}

Ese atributo priority está haciendo más de lo que piensas. Agrega fetchpriority="high" al HTML, desactiva lazy loading, y genera una etiqueta de preload <link> en el <head>. Volveremos a por qué esto importa para LCP.

La Config Que la Mayoría de las Personas Nunca Toca

Tu next.config.js (o next.config.ts si has migrado) tiene una clave images que controla todo:

// next.config.js
module.exports = {
  images: {
    formats: ['image/avif', 'image/webp'],
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
    minimumCacheTTL: 31536000, // 1 año en segundos
    dangerouslyAllowSVG: false,
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'your-cms.com',
        pathname: '/assets/**',
      },
    ],
  },
};

El orden del array formats importa. Next.js intentará AVIF primero, luego retrocederá a WebP. La codificación AVIF es más lenta pero produce archivos más pequeños -- este trade-off es importante entender.

El Problema de LCP: Por Qué Tu Imagen Hero Está Matando Tu Puntuación

Aquí está el escenario que veo en casi toda auditoría: una imagen hero hermosa, arriba del pliegue, que toma 3+ segundos para pintarse. El desarrollador usó next/image, pensó que estaba hecho, y siguió adelante. Pero la puntuación es terrible.

Los sospechosos usuales:

1. Faltando el Atributo `priority`

Por defecto, next/image carga todo con pereza. Eso es excelente para imágenes debajo del pliegue pero catastrófico para tu elemento LCP. Sin priority, el navegador descubre la imagen tarde, después de que JavaScript se ha hidratado y el intersection observer se activa.

// ❌ Esto carga con pereza tu imagen LCP
<Image src="/hero.jpg" alt="Hero" width={1200} height={630} />

// ✅ Esto la precarga
<Image src="/hero.jpg" alt="Hero" width={1200} height={630} priority />

2. Sobre-Comprimiendo con Valores Bajos de Calidad

He visto equipos establecer quality={50} pensando que más pequeño = más rápido. Pero si la imagen se ve borrosa, el algoritmo de LCP de Chrome aún tiene que esperar a que se pinte completamente. Y en pantallas de alto DPI, la calidad por debajo de 70 a menudo desencadena artefactos visibles que hacen que el diseño se vea barato.

Mi regla empírica: calidad 75-85 para fotos, calidad 90+ para imágenes con texto o bordes afilados.

3. No Usar `sizes` Correctamente

El atributo sizes le dice al navegador qué ancho de imagen solicitar antes de que se analice el CSS. Sin él, Next.js por defecto usa 100vw, lo que significa que los dispositivos móviles descargan imágenes de tamaño de escritorio.

<Image
  src="/hero.jpg"
  alt="Hero"
  fill
  priority
  sizes="(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px"
/>

Este cambio de prop único me ha dado las mayores mejoras de LCP -- a veces 400-800ms en móvil.

Next.js Image Optimization for Core Web Vitals in 2026 - architecture

Guerras de Formatos: AVIF vs WebP vs JPEG XL en 2026

El panorama de formatos se ha asentado considerablemente. Aquí es donde estamos:

Formato Soporte de Navegador (2026) Compresión Velocidad de Codificación Mejor Para
AVIF ~95% globalmente Excelente (30-50% más pequeño que WebP) Lento Fotos, imágenes hero
WebP ~98% globalmente Bueno (25-35% más pequeño que JPEG) Rápido Propósito general
JPEG XL ~45% (Chrome lo eliminó) Excelente Medio No recomendado para web
JPEG Universal Línea base Rápido Solo respaldo
PNG Universal Pobre para fotos Rápido Transparencia, screenshots

JPEG XL tenía una especificación prometedora, pero la decisión de Chrome de eliminar el soporte a fines de 2023 efectivamente lo mató para uso web. Safari agregó soporte, Firefox tiene soporte parcial, pero no puedes contar con él.

Mi recomendación: Establece formats: ['image/avif', 'image/webp'] y olvídalo. Next.js maneja la negociación de contenido automáticamente a través del encabezado Accept.

El Costo de Codificación de AVIF

Aquí hay algo que la documentación no enfatiza lo suficiente: la codificación AVIF es intensiva en CPU. En una primera solicitud a tu servidor Next.js, codificar una imagen AVIF de 1200px puede tomar 2-5 segundos en un servidor modesto. Ese primer visitante come el costo.

Estrategias para mitigar esto:

  • Pre-generar en tiempo de compilación usando next export o scripts de compilación personalizados
  • Usar un CDN con optimización de imágenes incorporada (Cloudflare Images, Imgix, Cloudinary)
  • Calentar tu caché después del despliegue con un script que golpea todas las URLs de imagen críticas
# Script simple de calentamiento de caché
#!/bin/bash
URLs=("https://yoursite.com/_next/image?url=%2Fhero.jpg&w=1200&q=80"
      "https://yoursite.com/_next/image?url=%2Fhero.jpg&w=750&q=80")

for url in "${URLs[@]}"; do
  curl -s -o /dev/null -H "Accept: image/avif,image/webp" "$url"
  echo "Warmed: $url"
done

Imágenes Responsivas Hecho Correctamente

Las imágenes responsivas en Next.js no son difíciles, pero requieren entender cómo deviceSizes, imageSizes, y el atributo sizes trabajan juntos.

El Patrón de Layout `fill`

Para imágenes donde no conoces la relación de aspecto en tiempo de compilación (contenido CMS, cargas de usuario), usa el atributo fill:

<div className="relative aspect-[16/9] w-full">
  <Image
    src={post.featuredImage}
    alt={post.title}
    fill
    className="object-cover"
    sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
  />
</div>

El div padre con posicionamiento relative y una relación de aspecto es crítico. Sin eso, las imágenes fill colapsan a altura cero y obtendrás una puntuación de CLS que te hará crisparte.

Dirección de Arte con ``

A veces necesitas diferentes cultivos para diferentes tamaños de pantalla. next/image no soporta <picture> nativamente, pero puedes trabajar alrededor de ello:

// Workaround de dirección de arte
export function ResponsiveHero({ mobileSrc, desktopSrc, alt }) {
  return (
    <>
      <div className="block md:hidden relative aspect-[9/16] w-full">
        <Image src={mobileSrc} alt={alt} fill priority sizes="100vw" />
      </div>
      <div className="hidden md:block relative aspect-[16/9] w-full">
        <Image src={desktopSrc} alt={alt} fill priority sizes="100vw" />
      </div>
    </>
  );
}

Sí, esto descarga el HTML de ambas imágenes, pero solo una se renderiza, y la oculta no cargará gracias a los defaults de lazy loading (aplicarías priority solo a la que coincide con el viewport).

Estrategias de Optimización de CDN y Edge

Si estás auto-hospedando Next.js (y muchos equipos lo hacen), necesitas una estrategia de CDN para imágenes.

Opción 1: Dejar Que Vercel Lo Maneje

La optimización de imágenes de Vercel se ejecuta en el edge. Para la mayoría de los proyectos, este es el camino más fácil. A partir de 2026, el plan Pro de Vercel incluye 5,000 imágenes fuente con optimización, con imágenes adicionales a $5 por 1,000. Los planes Enterprise tienen precios personalizados.

Opción 2: Servicio Externo de Optimización de Imágenes

Cloudinary, Imgix, y Cloudflare Images todos funcionan con Next.js a través del atributo loader o un loader personalizado:

// next.config.js con Cloudinary
module.exports = {
  images: {
    loader: 'custom',
    loaderFile: './lib/cloudinary-loader.js',
  },
};
// lib/cloudinary-loader.js
export default function cloudinaryLoader({ src, width, quality }) {
  const params = [
    `w_${width}`,
    `q_${quality || 'auto'}`,
    'f_auto',
    'c_limit',
  ];
  return `https://res.cloudinary.com/your-cloud/image/upload/${params.join(',')}${src}`;
}
Servicio Capa Gratuita Precios Pro (2026) Nodos Edge Soporte AVIF
Cloudinary 25 créditos/mes $89/mes (25GB) 60+
Imgix Ninguno $100/mes (100GB) Global
Cloudflare Images Ninguno $5/mes (100K variantes) 310+
Vercel (incorporado) 1,000 imágenes (Hobby) Incluido en Pro Edge

Para nuestros proyectos de desarrollo de CMS headless, típicamente usamos Cloudinary o el pipeline de imagen incorporado del CMS (Sanity, Contentful, e Hygraph todos tienen API de imagen decentes).

Opción 3: Cloudflare Polish + Next.js

Si ya estás detrás de Cloudflare, su característica Polish puede manejar la conversión de formato en el edge. Deshabilitarías la optimización de imágenes de Next.js y dejarías que Cloudflare haga el trabajo:

module.exports = {
  images: {
    unoptimized: true, // Dejar que Cloudflare lo maneje
  },
};

No soy un gran fan de este enfoque porque pierdes el dimensionamiento responsivo que proporciona next/image, pero funciona para configuraciones más simples.

Midiendo Lo Que Importa: Herramientas y Benchmarks

No puedes mejorar lo que no mides. Aquí está mi stack de pruebas:

Herramientas de Laboratorio

  • Chrome DevTools Lighthouse (v12 a partir de 2026): Aún el punto de partida. Ejecútalo en incógnito sin extensiones.
  • WebPageTest: Establécelo en Dulles, VA en un Moto G Power con 4G. Esto representa un usuario "lento" realista.
  • Unlighthouse: Escanea en masa tu sitio completo. Increíble para atrapar páginas que olvidaste.

Datos de Campo

  • Chrome UX Report (CrUX): Los datos reales que Google usa para señales de ranking. Disponible en PageSpeed Insights y BigQuery.
  • web-vitals.js: Agrega esto a tu aplicación para recopilar métricas de usuarios reales:
// app/layout.tsx
import { onLCP, onINP, onCLS } from 'web-vitals';

if (typeof window !== 'undefined') {
  onLCP(console.log);
  onINP(console.log);
  onCLS(console.log);
}

En producción, envía estos a tu plataforma de análisis en lugar de console.log. Usamos una combinación de Vercel Speed Insights y un endpoint personalizado que escribe en BigQuery.

Objetivos de Benchmark para 2026

Basado en los sitios que hemos auditado este año, aquí es lo que "bueno" se ve para sitios Next.js con muchas imágenes:

  • LCP en móvil (p75): < 1.8s (te da búfer bajo el umbral de 2.0s)
  • Peso total de imagen arriba del pliegue: < 200KB
  • Tiempo de carga de imagen hero: < 800ms en 4G
  • CLS de imágenes: 0

Técnicas Avanzadas Que Realmente Mueven la Aguja

Placeholders de Desenfoque con BlurHash

Next.js soporta placeholder="blur" nativamente para importaciones estáticas. Para imágenes dinámicas (de un CMS), necesitarás generar URLs de datos de desenfoque:

import { getPlaiceholder } from 'plaiceholder';

export async function getStaticProps() {
  const { base64 } = await getPlaiceholder('/path/to/image.jpg');
  return {
    props: { blurDataURL: base64 },
  };
}

// En el componente
<Image
  src={dynamicUrl}
  alt="Dynamic image"
  fill
  placeholder="blur"
  blurDataURL={blurDataURL}
/>

Esto no mejora directamente el LCP, pero mejora dramáticamente el rendimiento percibido y previene CLS.

HTTP/3 e Early Hints

Si tu CDN soporta HTTP/3 (Cloudflare, Fastly, y Vercel todos lo hacen), puedes usar 103 Early Hints para comenzar a enviar la imagen LCP antes de que el documento HTML esté completamente generado:

// middleware.ts
import { NextResponse } from 'next/server';

export function middleware(request) {
  const response = NextResponse.next();
  
  if (request.nextUrl.pathname === '/') {
    response.headers.set(
      'Link',
      '</hero.avif>; rel=preload; as=image; type="image/avif"'
    );
  }
  
  return response;
}

Carga de Esqueleto con `content-visibility` de CSS

Para páginas largas con muchas imágenes, content-visibility: auto le dice al navegador que omita completamente la renderización de contenido fuera de pantalla:

.image-grid-item {
  content-visibility: auto;
  contain-intrinsic-size: 300px 200px; /* Tamaño estimado */
}

Esto redujo INP en 30-40ms en una página de listado de productos que optimizamos el trimestre pasado.

Errores Comunes Que Veo en Cada Auditoría

  1. Usar next/image para SVGs decorativos: Solo usa una etiqueta <img> o incrusta el SVG. El pipeline de optimización agrega sobrecarga para cero beneficio.

  2. Establecer unoptimized globalmente porque "las imágenes se ven borrosas": Corrige la configuración de calidad en su lugar. unoptimized omite todo.

  3. Olvidar texto alt: Esto no es solo accesibilidad -- la búsqueda de imágenes de Google genera tráfico, y necesita texto alt para indexar tus imágenes.

  4. No establecer minimumCacheTTL: El por defecto es 60 segundos. Eso significa que tu servidor reoptimiza la misma imagen cada minuto bajo carga. Establécelo a al menos 2592000 (30 días).

  5. Usar imágenes fuente masivas: Subir una foto DSLR de 6000x4000px y esperar que Next.js la maneje. Pre-procesa tus imágenes fuente a un máximo de 2x tu tamaño de pantalla más grande.

  6. Ignorar la pestaña Network: Abre DevTools, filtra por Img, ordena por tamaño. Encontrarás problemas en 30 segundos.

Si estás lidiando con estos problemas en un sitio de producción, ese es exactamente el tipo de problema que resolvemos. Revisa nuestros precios o comunícate directamente -- hacemos auditorías de rendimiento como contrataciones independientes.

FAQ

¿Convierte next/image automáticamente imágenes a AVIF?

Sí, si tienes 'image/avif' en tu array images.formats en next.config.js (está incluido por defecto desde Next.js 14). La conversión ocurre bajo demanda cuando un navegador envía un encabezado Accept que incluye image/avif. La primera solicitud es más lenta debido a la codificación, pero las solicitudes posteriores se sirven desde caché.

¿Cuánto reduce realmente AVIF el tamaño del archivo de imagen en comparación con WebP?

En nuestras pruebas en cientos de imágenes de producción, AVIF es en promedio 30-50% más pequeño que WebP con calidad visual equivalente, y 50-70% más pequeño que JPEG. Las ganancias son más dramáticas en contenido fotográfico. Para screenshots o imágenes con texto, la diferencia se estrecha a 15-25%.

¿Debo usar priority en múltiples imágenes? Úsalo con cuidado -- solo en imágenes que están genuinamente arriba del pliegue y visibles en la carga inicial. Agregar priority a más de 2-3 imágenes derrota el propósito porque el navegador no puede priorizar todo simultáneamente. Para tu imagen hero de la página de inicio y quizás un logo, eso es todo.

¿Por qué mi LCP sigue siendo lento incluso con next/image y priority?

La razón más común es que tu tiempo de respuesta del servidor (TTFB) está comiendo tu presupuesto. Si tu servidor Next.js toma 800ms para responder, solo tienes 1.2 segundos para que la imagen cargue y se pinte. Otros culpables: CSS que bloquea la renderización, grandes paquetes JavaScript que retrasan la hidratación, o la fuente de imagen está en un servidor de origen lento.

¿Puedo usar next/image con exportaciones estáticas (next export)?

No con la optimización incorporada. Las exportaciones estáticas requieren images.unoptimized: true o un loader personalizado apuntando a un servicio externo como Cloudinary o Imgix. Esta es una razón por la que a veces recomendamos Astro para sitios puramente estáticos -- su manejo de imágenes no requiere un servidor en ejecución.

¿Cómo manejo imágenes de un CMS headless con next/image?

Agrega el dominio de imagen del CMS a images.remotePatterns en tu configuración. La mayoría de las plataformas CMS headless (Sanity, Contentful, Storyblok, Hygraph) tienen sus propias APIs de transformación de imagen. Puedes usar las de ellas a través de un loader personalizado o dejar que Next.js maneje la optimización. Generalmente preferimos el pipeline nativo del CMS para proyectos CMS headless porque reduce la carga del servidor.

¿Cuál es el impacto de la optimización de imágenes en las señales de ranking de Core Web Vitals?

Google confirmó en 2025 que Core Web Vitals siguen siendo una señal de ranking, aunque la relevancia del contenido aún domina. Dicho esto, para consultas competitivas donde la calidad del contenido es similar en los resultados superiores, CWV puede ser el desempate. Hemos visto sitios moverse 3-8 posiciones después de arreglar problemas de LCP que eran principalmente causados por imágenes no optimizadas.

¿Debo hacer lazy-load de todas las imágenes debajo del pliegue?

Sí, y Next.js lo hace por defecto (a menos que agregues priority). El atributo loading="lazy" nativo es lo que next/image usa bajo el capó. No hay necesidad de una biblioteca de lazy loading basada en JavaScript más -- el lazy loading nativo del navegador ha sido estable en todos los navegadores principales desde 2022.