Optimización de imágenes en Next.js
Receta
Utiliza el componente next/image para optimizar imágenes automáticamente con carga diferida, dimensionamiento responsivo y formatos modernos (WebP/AVIF). Configura patrones remotos para fuentes de imágenes externas.
// app/components/optimized-image.tsx
import Image from "next/image";
// Imagen local (la importación estática habilita un placeholder de desenfoque automático)
import heroPhoto from "@/public/images/hero.jpg";
export function HeroImage() {
return (
<Image
src={heroPhoto}
alt="Un hermoso paisaje de montaña"
placeholder="blur"
priority
className="rounded-lg"
/>
);
}// Imagen remota con dimensiones explícitas
export function RemoteImage() {
return (
<Image
src="https://images.unsplash.com/photo-example"
alt="Foto de Unsplash"
width={800}
height={600}
className="rounded-lg"
/>
);
}// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "images.unsplash.com",
},
{
protocol: "https",
hostname: "cdn.pixabay.com",
},
],
},
};
export default nextConfig;Ejemplo en funcionamiento
Una galería de imágenes responsiva con placeholders desenfocados y un diseño listo para lightbox:
// app/components/image-gallery.tsx
import Image from "next/image";
interface GalleryImage {
src: string;
alt: string;
blurDataURL: string;
width: number;
height: number;
}
const images: GalleryImage[] = [
{
src: "https://images.unsplash.com/photo-1506744038136-46273834b3fb",
alt: "Lago de montaña al amanecer",
blurDataURL: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCA...",
width: 1200,
height: 800,
},
{
src: "https://images.unsplash.com/photo-1469474968028-56623f02e42e",
alt: "Sendero del bosque en otoño",
blurDataURL: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCA...",
width: 1200,
height: 800,
},
{
src: "https://images.unsplash.com/photo-1447752875215-b2761acb3c5d",
alt: "Cordillera montañosa brumosa",
blurDataURL: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCA...",
width: 1200,
height: 800,
},
];
export function ImageGallery() {
return (
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
{images.map((image, index) => (
<div key={image.src} className="relative aspect-[3/2] overflow-hidden rounded-xl">
<Image
src={image.src}
alt={image.alt}
fill
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
placeholder="blur"
blurDataURL={image.blurDataURL}
priority={index === 0}
className="object-cover transition-transform duration-300 hover:scale-105"
/>
</div>
))}
</div>
);
}// app/gallery/page.tsx
import { ImageGallery } from "@/app/components/image-gallery";
export default function GalleryPage() {
return (
<main className="mx-auto max-w-6xl px-4 py-8">
<h1 className="mb-6 text-3xl font-bold">Galería de fotos</h1>
<ImageGallery />
</main>
);
}Análisis profundo
Cómo funciona
next/imagesirve automáticamente imágenes en formatos modernos (WebP, AVIF) basándose en la compatibilidad del navegador a través de la API de optimización de imágenes integrada.- Las imágenes se cargan de forma diferida por defecto. Solo se descargan las imágenes visibles en la ventana gráfica (o cercanas a ella).
- La propiedad
prioritydeshabilita la carga diferida y precarga la imagen, lo que la hace ideal para imágenes LCP (Largest Contentful Paint) sobre el pliegue. - El modo
fillhace que la imagen llene su contenedor principal. El padre debe tenerposition: relative(oabsoluteofixed) y dimensiones definidas. - La propiedad
sizesle dice al navegador qué ancho de imagen solicitar en cada punto de quiebre de ventana gráfica. Sin ella, Next.js sirve la imagen de tamaño completo a todos los dispositivos. - Las importaciones estáticas (p. ej.,
import hero from "@/public/hero.jpg") proporcionan automáticamentewidth,heightyblurDataURL. - Las imágenes remotas requieren propiedades
widthyheight(ofill) porque Next.js no puede inspeccionarlas en tiempo de compilación.
Variaciones
Modo fill con contenedor de relación de aspecto:
<div className="relative aspect-video w-full">
<Image
src="/images/banner.jpg"
alt="Banner"
fill
sizes="100vw"
className="object-cover"
/>
</div>Tamaños responsivos para diferentes diseños:
// Hero de ancho completo
<Image src={src} alt={alt} fill sizes="100vw" />
// Cuadrícula de dos columnas
<Image src={src} alt={alt} fill sizes="(max-width: 768px) 100vw, 50vw" />
// Tres columnas con barra lateral
<Image src={src} alt={alt} fill sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw" />Cargador personalizado para CDN externo:
import Image from "next/image";
const cloudinaryLoader = ({ src, width, quality }: { src: string; width: number; quality?: number }) => {
return `https://res.cloudinary.com/demo/image/upload/w_${width},q_${quality || 75}/${src}`;
};
export function CloudinaryImage() {
return (
<Image
loader={cloudinaryLoader}
src="sample.jpg"
alt="Imagen de Cloudinary"
width={800}
height={600}
/>
);
}Notas de TypeScript
next/imageexporta los tiposImagePropsyStaticImageData.- Las importaciones estáticas devuelven
StaticImageDatacon propiedadessrc,width,heightyblurDataURL. - La función
loaderse escribe comoImageLoaderdesdenext/image.
import type { ImageProps, StaticImageData } from "next/image";
interface HeroProps {
image: StaticImageData;
alt: string;
priority?: boolean;
}Trampas
- Usar
fillsinsizesfuerza al navegador a descargar la variante de imagen más grande. Siempre emparejafillcon una propiedadsizesapropiada. - El padre de una imagen
filldebe tenerposition: relativey dimensiones definidas. Sin esto, la imagen se colapsa a altura cero. placeholder="blur"con imágenes remotas requiere una propiedadblurDataURL. Solo las importaciones estáticas generan esto automáticamente.- La propiedad
prioritysolo debe usarse en imágenes sobre el pliegue (típicamente una o dos por página). Usarla en exceso anula los beneficios de la carga diferida. remotePatternsennext.configrequiere un reinicio del servidor para tomar efecto. Los cambios no se recogen con recarga en caliente.- Establecer
widthyheightno recorta ni redimensiona el elemento imagen en el DOM. Estos valores definen la relación de aspecto y el tamaño de solicitud. Usa CSS para dimensionamiento visual. - Las imágenes de dominios no listados en
remotePatternsfallarán con un error 400 en tiempo de ejecución, no en tiempo de compilación.
Alternativas
| Enfoque | Ventajas | Desventajas |
|---|---|---|
| next/image | Optimización integrada, carga diferida, conversión de formato | Props complejas, dependencia del lado del servidor |
| Etiqueta img nativa | Simple, sin configuración | Sin optimización, sin carga diferida por defecto |
| Cloudinary o Imgix | Transformaciones avanzadas, entrega de CDN | Servicio externo, costo adicional |
| @unpic/react | Agnóstico del framework, funciona con múltiples CDN | Sin servidor de optimización integrado |
Preguntas frecuentes
¿Qué formatos sirve automáticamente next/image y cómo decide?
- Sirve WebP o AVIF basándose en el encabezado
Acceptdel navegador. - La API de optimización de imágenes integrada maneja la conversión de formato automáticamente.
- No se requiere configuración; funciona de forma inmediata.
¿Cuándo debes usar la propiedad priority en una imagen?
- Usa
prioritysolo en imágenes LCP (Largest Contentful Paint) sobre el pliegue. - Típicamente una o dos imágenes por página.
- Deshabilita la carga diferida y precarga la imagen.
- Usarla en exceso anula los beneficios de la carga diferida.
Trampa: ¿Qué pasa si usas fill sin una propiedad sizes?
- El navegador descarga la variante de imagen más grande para todos los dispositivos.
- Siempre empareja
fillcon una propiedadsizesapropiada para servir imágenes correctamente dimensionadas por punto de quiebre.
¿Qué requisito CSS necesita un elemento padre para imágenes en modo fill?
- El padre debe tener
position: relative(oabsoluteofixed). - También debe tener dimensiones definidas (ancho/alto o relación de aspecto).
- Sin esto, la imagen se colapsa a altura cero.
¿Cómo difieren las importaciones estáticas de las imágenes remotas en next/image?
- Las importaciones estáticas proporcionan automáticamente
width,heightyblurDataURL. - Las imágenes remotas requieren propiedades explícitas
widthyheight(ofill) ya que Next.js no puede inspeccionarlas en tiempo de compilación. placeholder="blur"funciona automáticamente con importaciones estáticas pero necesita unblurDataURLmanual para imágenes remotas.
¿Cómo configuras next/image para permitir imágenes desde un dominio externo?
// next.config.ts
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{ protocol: "https", hostname: "images.unsplash.com" },
],
},
};Se requiere un reinicio del servidor después de los cambios.
Trampa: ¿Recortan o redimensionan las propiedades width y height la imagen en el DOM?
- No. Definen la relación de aspecto y el tamaño de imagen solicitado desde el servidor de optimización.
- Usa clases CSS para dimensionamiento visual en el navegador.
¿Cómo usas un cargador personalizado para un CDN externo como Cloudinary?
const cloudinaryLoader = ({ src, width, quality }: { src: string; width: number; quality?: number }) => {
return `https://res.cloudinary.com/demo/image/upload/w_${width},q_${quality || 75}/${src}`;
};
<Image loader={cloudinaryLoader} src="sample.jpg" alt="Foto" width={800} height={600} />¿Qué tipos de TypeScript exporta next/image para escribir props de imagen?
import type { ImageProps, StaticImageData } from "next/image";StaticImageDataes el tipo de retorno de importaciones estáticas, consrc,width,heightyblurDataURL.ImageLoaderescribe la funciónloaderpersonalizada.
¿Cuál es el valor correcto de sizes para una cuadrícula de tres columnas con una barra lateral?
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"Esto le dice al navegador que solicite imágenes de ancho completo en móvil, la mitad en tablet, y un tercio en escritorio.
¿Qué error obtienen las imágenes de dominios no en remotePatterns?
- Un error 400 en tiempo de ejecución, no en tiempo de compilación.
- La imagen simplemente falla al cargar cuando se visita la página.
- Añade el dominio a
remotePatternsennext.config.tse reinicia el servidor.