Guía completa de optimización de imágenes en Next.js: uso correcto del componente Image

Puntuación en Lighthouse: 62 puntos.
Pasé dos semanas en un proyecto Next.js y la puntuación de rendimiento apenas rozaba el aprobado. Abrí Performance y el problema saltaba a la vista: LCP (Largest Contentful Paint) de 4,8 segundos, casi todo por culpa de las imágenes.
Me quedé perplejo. Si ya usaba Next.js, ¿no venía con optimizaciones integradas? Revisé el código y la Hero de la home seguía con la etiqueta <img alt=""> más básica. En la lista de productos del e-commerce, decenas de imágenes se servían en tamaño original desde el servidor, 3-4 MB cada una.
Luego supe que Next.js es potente, pero la optimización de imágenes exige usar activamente su componente Image. Bien aplicado, el volumen puede bajar un 60-80 % y el LCP pasar de 4 segundos a menos de 2.
El problema es que muchos, como yo, siguen tropezando con Image: imágenes remotas con error «Un-configured Host», saltos al cargar, montones de opciones sin saber cuáles usar. Aquí recopilo los fallos que cometí y cómo los resolví, desde cero hasta un uso correcto del componente Image de Next.js.
¿Por qué usar el componente Image de Next.js?
Tres problemas de la etiqueta img normal
Puede que pienses que una imagen es solo <img alt="" src="xxx">. Yo también lo creía hasta que las pruebas de rendimiento mostraron lo contrario.
Primer problema: sin optimización de formato, ancho de banda desperdiciado
La etiqueta img muestra el formato que le des. Subes un PNG de 3 MB y el usuario descarga 3 MB. Hoy casi todos los navegadores soportan WebP (≈30 % menos que JPEG) y AVIF (≈40 % menos). img no hace nada al respecto: carga el archivo tal cual.
Segundo problema: una sola imagen para todas las pantallas
En móvil esto duele más. Subes una imagen de 2000×1500 px y en un móvil de 375 px de ancho el usuario descarga la imagen completa y el navegador la reduce. Tráfico y tiempo perdidos.
Tercer problema: desplazamiento de layout, muy molesto
Seguro que te ha pasado: vas a pulsar un botón y, al cargar una imagen, la página salta y clicas donde no debías. Eso es CLS (Cumulative Layout Shift), una de las Core Web Vitals de Google y factor directo de SEO.
Capacidades automáticas del componente Image
El componente Image de Next.js existe para resolver esto. No es un simple envoltorio de img, sino una solución completa de optimización.
Selección automática de formato
Image revisa la cabecera Accept del navegador y elige el formato soportado: AVIF si puede, WebP si no, y el original como último recurso. Todo automático, sin código extra.
En mis pruebas, un JPEG de 500 KB pasó a 180 KB en WebP y 120 KB en AVIF. Con decenas o cientos de imágenes en un sitio, el ahorro es enorme.
Carga responsiva
Image genera y sirve tamaños según la pantalla del dispositivo: 375 px en móvil, 1920 px en escritorio. Es el comportamiento de srcset, pero sin configurarlo a mano.
Lazy loading
Por defecto, Image solo carga imágenes visibles en el viewport. Las del final de la página esperan al scroll. Eso reduce mucho la carga inicial.
Con un uso correcto, el volumen de imágenes puede bajar un 60-80 %, el LCP quedar bajo 2,5 s y el CLS casi en cero. No son cifras teóricas: las medí en proyectos reales.
Uso básico: imágenes locales vs remotas
Al empezar con Image, lo que más confunde (a mí también) es por qué unas imágenes funcionan y otras no. La diferencia principal está entre locales y remotas.
Imágenes locales: el escenario más simple
Son archivos dentro del proyecto. Hay dos formas habituales.
Opción 1: import (recomendado)
import heroImage from '/public/images/hero.jpg'
import Image from 'next/image'
export default function Home() {
return (
<Image
src={heroImage}
alt="Hero image"
/>
)
}
Es lo más cómodo: Next.js lee ancho y alto en build time y no hace falta width ni height. Es lo que uso casi siempre con imágenes locales.
Opción 2: ruta directa
<Image
src="/images/hero.jpg"
width={1920}
height={1080}
alt="Hero image"
/>
Si la imagen está en public, puedes usar la ruta. Pero debes indicar ancho y alto manualmente; si no, error.
Imágenes remotas: donde más se tropieza
Son URLs externas, por ejemplo en almacenamiento en la nube. Aquí suelen aparecer los problemas.
Error habitual: «Un-configured Host»
<Image
src="https://images.unsplash.com/photo-123456"
width={800}
height={600}
alt="Sample image"
/>
Así, casi seguro verás:
Error: Invalid src prop (https://images.unsplash.com/photo-123456) on `next/image`,
hostname "images.unsplash.com" is not configured under images in your `next.config.js`
¿Por qué? Next.js evita que alguien abuse de tu servidor para optimizar URLs arbitrarias. Debes declarar qué dominios están permitidos.
Solución correcta: configurar remotePatterns
En next.config.js (recomendado en Next.js 14+):
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'images.unsplash.com',
port: '',
pathname: '/**',
},
{
protocol: 'https',
hostname: 's3.amazonaws.com',
port: '',
pathname: '/my-bucket/**',
},
],
},
}
module.exports = nextConfig
Significado de cada campo:
protocol: https o http, normalmente httpshostname: dominio, debe coincidir exactamenteport: puerto, suele dejarse vacíopathname: patrón de ruta;/**es todo, o puedes restringir
Tras cambiar la config, reinicia el servidor de desarrollo. La primera vez olvidé reiniciar y perdí media hora pensando que no funcionaba.
Configuración antigua (no recomendada)
Algunos tutoriales usan domains:
module.exports = {
images: {
domains: ['images.unsplash.com', 's3.amazonaws.com'],
},
}
En Next.js 14+ está obsoleta. Sigue funcionando, pero remotePatterns es más seguro porque limita rutas concretas.
Por qué hay que indicar ancho y alto
Salvo con import, debes definir width y height en locales y remotas. Es para evitar CLS.
Antes de cargar la imagen, el navegador necesita saber el espacio que ocupará. Sin dimensiones, espera a descargar y luego mueve el layout.
Si la imagen es responsiva y el ancho cambia con la pantalla, la propiedad fill lo resuelve más adelante.
Resolver el desplazamiento de layout (optimización CLS)
Una página que salta al cargar imágenes frustra. En un sitio de noticias, la queja más común era «iba a pulsar el titular y la imagen me mandó al anuncio». Estudiar CLS me hizo ver lo importante que es.
Qué es CLS y por qué importa
CLS (Cumulative Layout Shift) mide cuánto se mueven los elementos durante la carga.
Google lo incluye en Core Web Vitals y afecta al SEO. Por encima de 0,1 es malo; por debajo, aceptable. Con 15-20 imágenes que empujan el contenido, el CLS se dispara.
En experiencia de usuario, una página inestable suele cerrarse antes de leer nada.
Cómo evita CLS el componente Image
La idea es simple: reservar espacio con antelación.
Con width y height, el navegador deja un hueco antes de descargar la imagen. Al cargar, solo rellena ese hueco sin mover nada más.
<Image
src="/product.jpg"
width={400}
height={300}
alt="Product image"
/>
El navegador dibuja un marco 400×300 y luego carga la imagen. CLS ≈ 0.
En diseño responsivo, ancho y alto fijos no bastan. Ahí entra fill.
Imágenes responsivas: la propiedad fill
fill hace que la imagen llene el contenedor padre; el tamaño lo controla el CSS.
<div style={{ position: 'relative', width: '100%', height: '400px' }}>
<Image
src="/hero.jpg"
fill
style={{ objectFit: 'cover' }}
alt="Hero image"
/>
</div>
Puntos clave:
- El padre debe tener
position: relative - El padre necesita altura explícita (no
height: auto) objectFitcontrola el ajuste:coverrecorta para llenar;containmuestra todo, puede dejar bandas
Con altura definida en el padre, el espacio se reserva y el CLS se evita igual.
sizes: decir al navegador qué tamaño cargar
Con fill, conviene sizes; si no, Next.js no sabe qué tamaños generar.
<div style={{ position: 'relative', width: '100%', height: '400px' }}>
<Image
src="/hero.jpg"
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
style={{ objectFit: 'cover' }}
alt="Hero image"
/>
</div>
Significado de sizes:
- ≤768 px: imagen al 100 % del viewport
- 768-1200 px: 50 % del viewport
-
1200 px: 33 % del viewport
Next.js genera varios tamaños y el navegador elige el adecuado. En móvil no se descarga la versión de escritorio.
Casos prácticos por escenario
Hero (imagen a ancho completo above the fold)
<div style={{ position: 'relative', width: '100%', height: '60vh' }}>
<Image
src="/hero.jpg"
fill
priority
sizes="100vw"
style={{ objectFit: 'cover' }}
alt="Hero image"
/>
</div>
priority carga la Hero primero (lo veremos después). sizes="100vw": siempre ancho completo.
Miniatura de artículo (tamaño fijo)
<Image
src={post.thumbnail}
width={300}
height={200}
alt={post.title}
/>
Tamaño fijo: width y height son lo más simple.
Lista de productos (grid responsivo)
<div style={{ position: 'relative', width: '100%', paddingBottom: '100%' }}>
<Image
src={product.image}
fill
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
style={{ objectFit: 'cover' }}
alt={product.name}
/>
</div>
paddingBottom: '100%' crea un contenedor cuadrado 1:1. En móvil una columna, tablet dos, escritorio tres.
Configuración clave de rendimiento
Con lo básico y CLS cubiertos, estas opciones suben el rendimiento un escalón más.
priority: imágenes críticas del primer pantallazo
Por defecto, Image hace lazy loading. Pero la Hero, el logo u otros elementos clave deben cargarse ya, no en blanco.
Usa priority:
<Image
src="/hero.jpg"
width={1920}
height={1080}
priority
alt="Hero image"
/>
Con priority, Next.js:
- Quita el lazy loading y carga al instante
- Inserta un preload en
<head> - Mejora el LCP de forma notable
En un proyecto, añadir priority a la Hero bajó el LCP de 3,8 s a 2,1 s.
¿Cuándo usar priority?
- Hero de la home
- Logo (si es grande)
- Imagen principal del artículo
- Cualquier imagen que pueda ser el elemento LCP
¿Poner priority en todas? No. Forzarías muchas descargas a la vez y empeorarías el rendimiento. Resérvalo para lo esencial.
Cambio en Next.js 16
En Next.js 16 (RC), priority pasa a preload:
<Image
src="/hero.jpg"
width={1920}
height={1080}
preload
alt="Hero image"
/>
O con más control:
<Image
src="/hero.jpg"
width={1920}
height={1080}
loading="eager"
fetchPriority="high"
alt="Hero image"
/>
loading: estrategia de carga
Dos valores:
lazy(por defecto): carga al entrar en el viewporteager: carga de inmediato
En la mayoría de casos, lazy basta. Solo las críticas del primer pantallazo necesitan eager.
// Imagen al final: lazy por defecto
<Image src="/related-1.jpg" width={300} height={200} alt="Related post" />
// Contenido principal above the fold: eager
<Image src="/main-content.jpg" width={800} height={600} loading="eager" alt="Main content" />
quality: equilibrio entre calidad y tamaño
quality va de 1 a 100; el valor por defecto es 75.
<Image
src="/product.jpg"
width={800}
height={600}
quality={90}
alt="Product image"
/>
Más calidad = más peso. Valores de referencia:
- Imágenes clave del primer pantallazo:
quality={90} - Contenido general:
quality={75}(por defecto) - Miniaturas y fondos:
quality={60}
De 90 a 75 casi no se nota a simple vista, pero el archivo baja ~30 %. De 75 a 60 suele ser aceptable y ahorra otro ~20 %.
Actualización importante: desde Next.js 16, quality puede ser obligatorio para evitar abusos vía parámetros URL. Tras actualizar, revisa que cada Image lo tenga.
Selección automática de formato: WebP vs AVIF
Es automática. Next.js mira Accept y elige:
- AVIF si el navegador lo soporta (más pequeño, codificación más lenta)
- WebP si no AVIF (buen equilibrio)
- Formato original si ninguno
AVIF suele ser 30-40 % más pequeño que WebP. Chrome 85+, Firefox 93+ y Safari 16+ ya lo soportan.
Ejemplos de configuración en la práctica
Hero de home (prioridad y alta calidad)
<div style={{ position: 'relative', width: '100%', height: '60vh' }}>
<Image
src="/hero.jpg"
fill
priority
quality={90}
sizes="100vw"
style={{ objectFit: 'cover' }}
alt="Welcome to our site"
/>
</div>
Miniaturas de listado (lazy, calidad media)
{posts.map(post => (
<Image
key={post.id}
src={post.thumbnail}
width={300}
height={200}
quality={75}
alt={post.title}
/>
))}
Iconos del footer (lazy, baja calidad)
<Image
src="/footer-icon.png"
width={40}
height={40}
quality={60}
alt="Footer icon"
/>
Errores frecuentes y soluciones
Estos son los fallos que más he visto (y sufrido) en desarrollo real.
Error 1: Un-configured Host
El más común.
Mensaje:
Error: Invalid src prop (https://example.com/image.jpg) on `next/image`,
hostname "example.com" is not configured under images in your `next.config.js`
Causa:
URL externa en Image sin remotePatterns para ese dominio.
Solución:
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
port: '',
pathname: '/**',
},
],
},
}
Notas:
- Reinicia el servidor tras editar la config (
npm run dev) hostnamedebe coincidir exactamente; no admite comodines como*.example.com- Varios dominios = varios objetos en el array
Error 2: desplazamiento de layout / CLS alto
Síntoma:
Al cargar imágenes, el contenido salta hacia abajo.
Causas:
- Sin
widthyheight fillcon padre sin altura
Solución:
Caso 1: dimensiones explícitas
// ❌ Falta ancho y alto
<Image src="/product.jpg" alt="Product" />
// ✅ Correcto
<Image src="/product.jpg" width={400} height={300} alt="Product" />
Caso 2: altura en el padre
// ❌ Padre sin altura
<div style={{ position: 'relative', width: '100%' }}>
<Image src="/hero.jpg" fill alt="Hero" />
</div>
// ✅ Padre con altura
<div style={{ position: 'relative', width: '100%', height: '400px' }}>
<Image src="/hero.jpg" fill alt="Hero" />
</div>
Error 3: imagen borrosa o demasiado grande
Síntoma:
En móvil, borrosa o muy lenta.
Causa:
Sin sizes, Next.js asume 100vw. Si la imagen ocupa la mitad de pantalla, descargas el doble de lo necesario.
Solución:
<Image
src="/product.jpg"
width={400}
height={300}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
alt="Product"
/>
Si el tamaño es fijo (miniatura), width y height bastan; no hace falta sizes.
Error 4: APIs obsoletas
Next.js 14 y 15 deprecaron varias cosas. Tutoriales viejos pueden mostrarlas.
Obsoleto 1: domains
// ❌ Obsoleto en Next.js 14+
module.exports = {
images: {
domains: ['example.com'],
},
}
// ✅ Usa remotePatterns
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
},
],
},
}
Obsoleto 2: onLoadingComplete
// ❌ Obsoleto en Next.js 14+
<Image
src="/image.jpg"
width={400}
height={300}
onLoadingComplete={() => console.log('loaded')}
alt="Image"
/>
// ✅ Usa onLoad
<Image
src="/image.jpg"
width={400}
height={300}
onLoad={() => console.log('loaded')}
alt="Image"
/>
Cambios en Next.js 16 (próximo):
// ⚠️ En Next.js 16 priority pasa a preload
// Antes (Next.js 15 y anteriores)
<Image src="/hero.jpg" width={1920} height={1080} priority alt="Hero" />
// Nuevo (Next.js 16)
<Image src="/hero.jpg" width={1920} height={1080} preload alt="Hero" />
// o
<Image src="/hero.jpg" width={1920} height={1080} loading="eager" fetchPriority="high" alt="Hero" />
Lista rápida de diagnóstico
- ¿Imágenes remotas con
remotePatterns? - ¿Reiniciaste el servidor tras cambiar la config?
- ¿
widthyheight(o altura en el padre confill)? - Con
fill: ¿padre conposition: relativey altura definida? - ¿
sizesrazonable en imágenes responsivas? - ¿APIs obsoletas (
domains,onLoadingComplete, etc.)?
Técnicas avanzadas y buenas prácticas
Placeholder para mejorar la experiencia
En redes lentas, un placeholder borroso ayuda mucho.
blur placeholder
import Image from 'next/image'
import heroImage from '/public/hero.jpg'
export default function Hero() {
return (
<Image
src={heroImage}
placeholder="blur"
alt="Hero image"
/>
)
}
Con import local, Next.js genera un base64 de baja calidad. placeholder="blur" muestra la versión borrosa hasta la carga final, como en Instagram o Medium.
blur en imágenes remotas
Hay que pasar blurDataURL manualmente:
<Image
src="https://example.com/image.jpg"
width={800}
height={600}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRg..."
alt="Remote image"
/>
Puedes generarlo con esta herramienta online o con sharp en el servidor.
empty placeholder
placeholder="empty" deja el área en blanco hasta cargar. Es el comportamiento por defecto.
Uso con CDN
Por defecto, Next.js optimiza con su Image Optimization API. Con Cloudinary, Uploadcare u otro CDN, configura un loader personalizado.
// next.config.js
module.exports = {
images: {
loader: 'cloudinary',
path: 'https://res.cloudinary.com/your-cloud-name/',
},
}
O un loader propio:
// next.config.js
module.exports = {
images: {
loader: 'custom',
loaderFile: './my-loader.js',
},
}
// my-loader.js
export default function myLoader({ src, width, quality }) {
return `https://cdn.example.com/${src}?w=${width}&q=${quality || 75}`
}
Todas las peticiones van a tu CDN, no al servidor Next.js. Útil en sitios con mucho tráfico.
Esquema completo de imágenes responsivas
No es solo redimensionar: también el contexto de visualización.
Estrategia móvil, tablet y escritorio
<div className="image-container">
<Image
src="/product.jpg"
width={1200}
height={800}
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
style={{
width: '100%',
height: 'auto',
}}
alt="Product image"
/>
</div>
CSS asociado:
.image-container {
width: 100%;
}
@media (min-width: 640px) {
.image-container {
width: 50%;
}
}
@media (min-width: 1024px) {
.image-container {
width: 33.333%;
}
}
Alinea sizes con las media queries del CSS para que el navegador elija el tamaño correcto.
Monitorización y depuración
Chrome DevTools
DevTools → pestaña Network → filtro Img. Verás tamaño, tiempo y cabeceras (formato).
Con Image, la URL lleva parámetros como ?w=xxx&q=xxx: ahí está la optimización de Next.js.
Lighthouse
DevTools → Lighthouse → «Analyze page load». Revisa:
- LCP: ideal < 2,5 s
- CLS: ideal < 0,1
- Sugerencias de imágenes: qué falta optimizar
Tras optimizar imágenes, suelo pasar de ~60 a 90+ puntos.
Monitorizar Core Web Vitals en producción
Google Search Console o Vercel Analytics ayudan a detectar regresiones de rendimiento.
Conclusión
El componente Image de Next.js resuelve tres cosas: carga lenta, errores de configuración y desplazamiento de layout.
Bien usado, obtienes:
- 60-80 % menos de volumen (WebP/AVIF automático)
- LCP bajo 2,5 s (
prioritydonde toca) - CLS casi cero (
width/heightofillcorrecto)
Configuraciones que conviene memorizar:
remotePatternspara remotas; reinicia el servidorwidthyheight, ofillcon altura en el padreprioritysolo en imágenes críticas; el resto en lazysizesen imágenes responsivasquality: 90 arriba, 75 general, 60 miniaturas
Revisa tu proyecto y cambia <img alt=""> por <Image>. Pasa Lighthouse y mira cuánto sube la puntuación. Apuesto a que al menos 20 puntos.
Si te atasacas, repasa la sección de errores frecuentes: casi siempre está ahí la respuesta. El componente Image tiene muchas opciones, pero con unas pocas bien dominadas cubres la mayoría de escenarios.
Flujo completo de optimización del componente Image de Next.js
Pasos completos desde configurar imágenes remotas hasta optimizar rendimiento y evitar desplazamiento de layout
⏱️ Estimated time: 2 hr
- 1
Step 1: Configurar dominios de imágenes remotas
En next.config.js:
• Añade el array images.remotePatterns
• Define protocol, hostname y pathname
• Soporta patrones con comodines
Ejemplo:
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
pathname: '/images/**'
}
]
}
Nota: tras configurar, reinicia el servidor de desarrollo - 2
Step 2: Sustituir img por el componente Image
Uso básico:
• Import: import Image from 'next/image'
• Define width y height (o usa fill)
• Añade alt (necesario para SEO)
Ejemplo:
<Image
src="/hero.jpg"
width={800}
height={600}
alt="Texto descriptivo"
/> - 3
Step 3: Gestionar el desplazamiento de layout (CLS)
Método 1: dimensiones fijas
• width y height obligatorios
• Usa aspect-ratio para mantener proporción
Método 2: modo fill
• Padre con position: relative
• Image con fill
• Padre con ancho y alto definidos
Método 3: placeholder
• blurDataURL: imagen borrosa de placeholder
• placeholder="blur": muestra el placeholder - 4
Step 4: Optimizar el rendimiento de carga
Imágenes críticas del primer pantallazo:
• Añade priority
• quality=90
• Asegúrate de que estén en el viewport
Otras imágenes:
• Lazy loading por defecto (sin configurar)
• quality=75 (equilibrio calidad/tamaño)
• sizes para responsividad
Miniaturas:
• quality=60 suele bastar
• Usa versiones pequeñas - 5
Step 5: Configurar imágenes responsivas
Usa sizes:
• Indica al navegador qué tamaño necesita en cada breakpoint
• El navegador elige la imagen adecuada del srcset
Ejemplo:
<Image
src="/hero.jpg"
width={1200}
height={630}
sizes="(max-width: 768px) 100vw, 50vw"
alt="Descripción"
/>
En móvil carga ancho completo; en escritorio, 50 % del viewport - 6
Step 6: Probar y validar
Pruebas de rendimiento:
• Lighthouse para LCP y CLS
• Pestaña Network para ver cargas
• Verifica formato (WebP/AVIF)
Checklist:
• Todas las remotas con dominio configurado
• Todas con width y height
• Imágenes del primer pantallazo con priority
• CLS cercano a 0
• Volumen de imágenes reducido más del 60 %
FAQ
¿Por qué las imágenes remotas dan error 'Un-configured Host'?
¿Es obligatorio definir width y height en Image?
¿Cómo evitar el desplazamiento al cargar imágenes?
¿Cuándo usar la propiedad priority?
¿Image convierte el formato automáticamente?
¿Para qué sirve la propiedad sizes?
¿Cómo optimizar la calidad de las imágenes?
14 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 del mecanismo de caché en Next.js: cuándo usar revalidate correctamente
Análisis profundo de las cuatro capas de caché en Next.js, cuándo usar revalidate, revalidatePath y revalidateTag, solución a problemas habituales como datos que no se actualizan, con guía completa de diagnóstico y mejores prácticas
Parte 25 de 51
Siguiente
Next.js Core Web Vitals en la práctica: guía completa de optimización LCP/FCP/CLS
Guía completa para optimizar LCP, FCP y CLS en Next.js y subir la puntuación de Lighthouse por encima de 90. Incluye más de 10 ejemplos de código, trampas habituales y consejos aplicados en producción.
Parte 27 de 51



Comentarios
Inicia sesión con GitHub para dejar un comentario