Optimización de imágenes
Sirve imágenes optimizadas y receptivas con next/image -- conversión automática de formato, carga diferida y sugerencias de tamaño.
Busca en todas las páginas de la documentación
Sirve imágenes optimizadas y receptivas con next/image -- conversión automática de formato, carga diferida y sugerencias de tamaño.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Tarjeta de referencia rápida -- lista para copiar y pegar.
import Image from "next/image";
// Imagen local (auto width/height desde la importación)
import heroImage from "@/public/hero.jpg";
<Image src={heroImage} alt="Hero banner" priority />
// Imagen remota (debe especificar width y height)
<Image
src="https://cdn.example.com/photo.jpg"
alt="Product photo"
width={800}
height={600}
/>
// Llenar contenedor (receptiva, sin dimensiones explícitas)
<div className="relative h-64 w-full">
<Image
src="/banner.jpg"
alt="Banner"
fill
className="object-cover"
sizes="100vw"
/>
</div>Cuándo usarlo: Cualquier momento que renderices una imagen. next/image maneja carga diferida, conversión de formato (WebP/AVIF), dimensionamiento receptivo y prevención de CLS fuera de la caja.
// app/gallery/page.tsx
import Image from "next/image";
type Photo = {
id: string;
url: string;
alt: string;
width: number;
height: number;
};
async function getPhotos(): Promise<Photo[]> {
const res = await fetch("https://api.example.com/photos", {
next: { revalidate: 3600 },
});
return res.json();
}
export default async function GalleryPage() {
const photos = await getPhotos();
return (
<main className="max-w-6xl mx-auto p-6">
<h1 className="text-3xl font-bold mb-8">Galería</h1>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{photos.map((photo, index) => (
<div key={photo.id} className="relative aspect-[4/3]">
<Image
src={photo.url}
alt={photo.alt}
fill
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
className="object-cover rounded-lg"
priority={index < 3} // prioriza imágenes por encima del pliegue
/>
</div>
))}
</div>
</main>
);
}// app/components/product-card.tsx
import Image from "next/image";
import Link from "next/link";
type Product = {
slug: string;
name: string;
price: number;
imageUrl: string;
};
export function ProductCard({ product }: { product: Product }) {
return (
<Link
href={`/products/${product.slug}`}
className="group block border rounded-lg overflow-hidden"
>
<div className="relative aspect-square bg-gray-50">
<Image
src={product.imageUrl}
alt={product.name}
fill
sizes="(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 25vw"
className="object-contain group-hover:scale-105 transition-transform"
/>
</div>
<div className="p-4">
<h3 className="font-medium">{product.name}</h3>
<p className="text-gray-600">${product.price.toFixed(2)}</p>
</div>
</Link>
);
}// next.config.ts -- configura dominios de imagen remota
import type { NextConfig } from "next";
const config: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "cdn.example.com",
pathname: "/images/**",
},
{
protocol: "https",
hostname: "*.unsplash.com",
},
],
formats: ["image/avif", "image/webp"], // prefiere AVIF, retroceso a WebP
},
};
export default config;Lo que esto demuestra:
fill para imágenes receptivas en un diseño de cuadrículasizes indicando al navegador cuán ancha será la imagen en diferentes tamaños de viewportpriority para imágenes por encima del pliegue (desactiva carga diferida, añade sugerencia de precarga)next.config.tsaspect-[4/3] y aspect-square para prevenir CLSnext/image renderiza una etiqueta <img> con atributos srcset y sizes. El navegador selecciona el mejor tamaño de imagen basado en el viewport y la relación de píxeles del dispositivo.priority desactiva la carga diferida y añade una <link rel="preload"> para imágenes LCP.fill hace que la imagen llene su contenedor padre (el padre debe ser position: relative, absolute, o fixed). Usa esto cuando no conozcas las dimensiones de la imagen en tiempo de compilación.width y height establecen la relación de aspecto intrínseca para prevenir Cumulative Layout Shift (CLS). No establecen el tamaño renderizado -- usa CSS para eso.Imagen receptiva con puntos de ruptura explícitos:
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px"
className="w-full h-auto"
/>Desenfoque de marcador de posición (imágenes locales):
import heroImg from "@/public/hero.jpg";
<Image
src={heroImg}
alt="Hero"
placeholder="blur" // genera automáticamente blurDataURL para imágenes locales
/>Desenfoque de marcador de posición (imágenes remotas):
<Image
src="https://cdn.example.com/photo.jpg"
alt="Photo"
width={800}
height={600}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQ..." // debes proporcionar manualmente
/>Deshabilitando la optimización (SVGs, GIFs animados):
<Image
src="/logo.svg"
alt="Logo"
width={200}
height={50}
unoptimized // sirve el archivo original tal cual
/>import Image, { type ImageProps } from "next/image";
// Extendiendo props de Image
type AvatarProps = Omit<ImageProps, "alt"> & {
name: string;
};
function Avatar({ name, ...props }: AvatarProps) {
return (
<Image
alt={`Avatar for ${name}`}
className="rounded-full"
{...props}
/>
);
}
// Tipo StaticImageData para imágenes importadas
import type { StaticImageData } from "next/image";
import fallback from "@/public/fallback.jpg";
function getImage(url?: string): string | StaticImageData {
return url ?? fallback;
}Falta la prop sizes con fill -- Sin sizes, el navegador asume que la imagen tiene 100vw de ancho, descargando un archivo innecesariamente grande. Solución: Siempre proporciona una prop sizes que coincida con tu diseño CSS.
Las imágenes remotas requieren remotePatterns -- Usar una URL remota sin configurar remotePatterns en next.config.ts lanza un error de compilación. Solución: Añade el nombre de host (y opcionalmente patrón de ruta) a images.remotePatterns.
fill requiere un padre posicionado -- Si el elemento padre no tiene position: relative (o absolute/fixed), la imagen se sale de su contenedor. Solución: Añade className="relative" al padre.
priority en demasiadas imágenes -- Marcar muchas imágenes como priority derrota el propósito y ralentiza la página. Solución: Solo usa priority en la imagen LCP (normalmente 1-2 imágenes por encima del pliegue).
Width y height no controlan el tamaño renderizado -- width={800} height={600} establece la relación de aspecto intrínseca, no el tamaño mostrado. La imagen puede renderizarse más pequeña o más grande dependiendo de CSS. Solución: Usa clases CSS (className) para controlar las dimensiones renderizadas.
La codificación AVIF es lenta -- AVIF produce archivos más pequeños pero tarda más en generarse en la primera solicitud. Solución: Acepta la latencia de arranque en frío, o elimina "image/avif" de images.formats si el tiempo de respuesta es crítico.
| Alternativa | Úsalo cuando | No lo uses cuando |
|---|---|---|
next/image | Todas las imágenes en una aplicación Next.js (la opción predeterminada) | Necesitas <img> crudo por una razón específica |
Etiqueta <img> nativa | Imágenes estáticas simples sin necesidades de optimización | Quieres WebP/AVIF automático, carga diferida y srcset |
Elemento <picture> | Necesitas dirección de arte (diferentes cultivos en diferentes tamaños) | El tamaño receptivo solo es suficiente |
| Cloudinary o Imgix | Necesitas transformaciones avanzadas (cultivo, marca de agua, detección facial) | La optimización integrada de Next.js es suficiente |
| SVG en línea | Iconos e ilustraciones que necesitan ser estilizados con CSS | Contenido fotográfico |
srcset y sizesfill cuando no conozcas las dimensiones de la imagen en tiempo de compilación (p. ej., imágenes cargadas por el usuario). La imagen llena su contenedor padre.width y height explícitos cuando las dimensiones se conocen. Estos establecen la relación de aspecto intrínseca, no el tamaño renderizado.<link rel="preload"> para que el navegador la obtenga inmediatamente.width y height establecen la relación de aspecto intrínseca para prevención de CLS, no el tamaño renderizado. Usa clases CSS (className) para controlar las dimensiones de visualización reales.
El elemento padre debe tener position: relative, absolute, o fixed. Añade className="relative" al <div> padre.
Sin sizes, el navegador asume que la imagen tiene 100vw de ancho y descarga un archivo innecesariamente grande. Proporciona sizes para que coincida con tu diseño CSS:
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"Añade remotePatterns a next.config.ts:
images: {
remotePatterns: [
{ protocol: "https", hostname: "cdn.example.com", pathname: "/images/**" },
],
}Para imágenes remotas, debes proporcionar blurDataURL manualmente (una imagen diminuta codificada en base64). Las imágenes locales obtienen generación de desenfoque automática con placeholder="blur".
import Image, { type ImageProps } from "next/image";
type AvatarProps = Omit<ImageProps, "alt"> & {
name: string;
};
function Avatar({ name, ...props }: AvatarProps) {
return <Image alt={`Avatar for ${name}`} {...props} />;
}StaticImageData es el tipo devuelto cuando importas un archivo de imagen local. Úsalo cuando una función o prop pueda aceptar tanto una importación local como una cadena de URL:
import type { StaticImageData } from "next/image";
function getImage(url?: string): string | StaticImageData {
return url ?? fallbackImage;
}Usa unoptimized para imágenes que no deben procesarse, como SVGs o GIFs animados. El archivo se sirve tal cual sin conversión de formato o cambio de tamaño.
AVIF produce archivos más pequeños pero la codificación es más lenta en la primera solicitud (arranque en frío). Si el tiempo de respuesta es crítico, considera eliminar "image/avif" de images.formats y usar solo WebP.
Revisado por Chris St. John·Última actualización: 19 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥