Search across all documentation pages
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Use the next/image component to automatically optimize images with lazy loading, responsive sizing, and modern formats (WebP/AVIF). Configure remote patterns for external image sources.
// app/components/optimized-image.tsx
import Image from "next/image";
// Local image (static import enables automatic blur placeholder)
import heroPhoto from "@/public/images/hero.jpg";
export function HeroImage() {
return (
<Image
src={heroPhoto}
alt="A scenic mountain landscape"
placeholder="blur"
priority
className="rounded-lg"
/>
);
}// Remote image with explicit dimensions
export function RemoteImage() {
return (
<Image
src="https://images.unsplash.com/photo-example"
alt="Unsplash photo"
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;A responsive image gallery with blur placeholders and a lightbox-ready layout:
// 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: "Mountain lake at sunrise",
blurDataURL: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCA...",
width: 1200,
height: 800,
},
{
src: "https://images.unsplash.com/photo-1469474968028-56623f02e42e",
alt: "Forest path in autumn",
blurDataURL: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCA...",
width: 1200,
height: 800,
},
{
src: "https://images.unsplash.com/photo-1447752875215-b2761acb3c5d",
alt: "Misty mountain range",
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">Photo Gallery</h1>
<ImageGallery />
</main>
);
}next/image automatically serves images in modern formats (WebP, AVIF) based on browser support via the built-in Image Optimization API.priority prop disables lazy loading and preloads the image, making it ideal for above-the-fold LCP (Largest Contentful Paint) images.fill mode makes the image fill its parent container. The parent must have position: relative (or absolute or fixed) and defined dimensions.sizes prop tells the browser which image width to request at each viewport breakpoint. Without it, Next.js serves the full-size image to all devices.import hero from "@/public/hero.jpg") automatically provide width, height, and blurDataURL.width and height props (or fill) because Next.js cannot inspect them at build time.Fill mode with aspect ratio container:
<div className="relative aspect-video w-full">
<Image
src="/images/banner.jpg"
alt="Banner"
fill
sizes="100vw"
className="object-cover"
/>
</div>Responsive sizes for different layouts:
// Full-width hero
<Image src={src} alt={alt} fill sizes="100vw" />
// Two-column grid
<Image src={src} alt={alt} fill sizes="(max-width: 768px) 100vw, 50vw" />
// Three-column with sidebar
<Image src={src} alt={alt} fill sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw" />Custom loader for external CDN:
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="Cloudinary image"
width={800}
height={600}
/>
);
}next/image exports ImageProps and StaticImageData types.StaticImageData with src, width, height, and blurDataURL properties.loader function is typed as ImageLoader from next/image.import type { ImageProps, StaticImageData } from "next/image";
interface HeroProps {
image: StaticImageData;
alt: string;
priority?: boolean;
}fill without sizes forces the browser to download the largest image variant. Always pair fill with an appropriate sizes prop.fill image must have position: relative and defined dimensions. Without this, the image collapses to zero height.placeholder="blur" with remote images requires a blurDataURL prop. Only static imports generate this automatically.priority prop should only be used on above-the-fold images (typically one or two per page). Overusing it defeats lazy loading benefits.remotePatterns in next.config requires a server restart to take effect. Changes are not picked up by hot reload.width and height does not crop or resize the image element in the DOM. These values define the aspect ratio and request size. Use CSS for visual sizing.remotePatterns will fail with a 400 error at runtime, not at build time.| Approach | Pros | Cons |
|---|---|---|
| next/image | Built-in optimization, lazy loading, format conversion | Complex props, server-side dependency |
| Native img tag | Simple, no configuration | No optimization, no lazy loading by default |
| Cloudinary or Imgix | Advanced transformations, CDN delivery | External service, additional cost |
| @unpic/react | Framework-agnostic, works with multiple CDNs | No built-in optimization server |
Accept header.priority only on above-the-fold LCP (Largest Contentful Paint) images.fill with an appropriate sizes prop to serve correctly sized images per breakpoint.position: relative (or absolute or fixed).width, height, and blurDataURL.width and height props (or fill) since Next.js cannot inspect them at build time.placeholder="blur" works automatically with static imports but needs a manual blurDataURL for remote images.// next.config.ts
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{ protocol: "https", hostname: "images.unsplash.com" },
],
},
};A server restart is required after changes.
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="Photo" width={800} height={600} />import type { ImageProps, StaticImageData } from "next/image";StaticImageData is the return type of static imports, with src, width, height, and blurDataURL.ImageLoader types the custom loader function.sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"This tells the browser to request full-width images on mobile, half on tablet, and one-third on desktop.
remotePatterns in next.config.ts and restart the server.Reviewed by Chris St. John·Last updated Jul 7, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥