Next.js Image Optimization — next/image Complete Guide
Advertisement
Introduction
The next/image component is one of Next.js's most impactful performance features. It automatically converts images to modern formats like WebP and AVIF, resizes them for each device, lazy loads offscreen images, and prevents Cumulative Layout Shift (CLS) by reserving space before the image loads. Using next/image instead of a plain img tag can reduce image payload by 60–80% and dramatically improve Core Web Vitals scores.
Why This Matters
Images are consistently the largest contributor to page weight — often 60–80% of total bytes transferred. A 3MB hero image served to a mobile device on a slow connection can cause a 5+ second LCP, immediately failing Google's Core Web Vitals threshold.
next/image solves this automatically: it serves WebP to Chrome (30% smaller than JPEG), AVIF to supported browsers (50% smaller than JPEG), resizes to the exact screen dimensions needed, and lazy loads everything below the fold. You get all this with a single component swap.
In 2025, LCP and CLS are both Google ranking signals. Proper image optimization is one of the highest-ROI SEO improvements you can make.
Basic Usage
import Image from 'next/image'
export default function ProductCard() {
return (
<Image
src="/images/product.jpg"
alt="Blue running shoes"
width={800}
height={600}
className="rounded-lg"
/>
)
}Always provide a descriptive alt attribute — it improves accessibility and contributes to image SEO.
fill for Responsive Layout Containers
When you do not know the image dimensions ahead of time, use fill with a positioned parent:
import Image from 'next/image'
export function HeroBanner() {
return (
<div className="relative w-full h-96">
<Image
src="/images/hero.jpg"
alt="Mountain landscape at sunrise"
fill
className="object-cover"
priority
/>
</div>
)
}The parent must have position: relative (or absolute/fixed). object-cover controls how the image fills the space.
priority for LCP Images
Mark your above-the-fold hero image with priority to disable lazy loading and add a preload link:
import Image from 'next/image'
export function Hero() {
return (
<Image
src="/images/hero.webp"
alt="Hero image"
width={1200}
height={630}
priority
/>
)
}Only use priority on images visible on initial load. Overusing it defeats its purpose.
Responsive Images with sizes
The sizes prop tells the browser how wide the image will be at each breakpoint:
import Image from 'next/image'
export function BlogCard({ post }: { post: Post }) {
return (
<div className="w-full md:w-1/2 lg:w-1/3">
<Image
src={post.coverImage}
alt={post.title}
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
className="object-cover"
/>
</div>
)
}Without sizes, Next.js defaults to 100vw, downloading a full-width image even on mobile.
Remote Images with remotePatterns
Configure allowed external domains in next.config.js:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'images.unsplash.com',
pathname: '/**',
},
{
protocol: 'https',
hostname: 'cdn.example.com',
pathname: '/uploads/**',
},
],
},
}
module.exports = nextConfigThen use the external URL in src directly:
<Image
src="https://images.unsplash.com/photo-1234567890"
alt="Photo from Unsplash"
width={800}
height={600}
/>placeholder blur for Perceived Performance
Show a blurred placeholder while the image loads:
import Image from 'next/image'
import productImage from '@/public/images/product.jpg'
export function ProductImage() {
return (
<Image
src={productImage}
alt="Product"
placeholder="blur"
// blurDataURL auto-generated for static imports
/>
)
}For dynamic remote images, provide a base64-encoded blur data URL:
<Image
src={product.imageUrl}
alt={product.name}
width={400}
height={400}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJ..."
/>Avatar and Circular Images
import Image from 'next/image'
export function Avatar({ user }: { user: { name: string; image: string } }) {
return (
<div className="relative w-12 h-12 rounded-full overflow-hidden">
<Image
src={user.image}
alt={`${user.name} avatar`}
fill
className="object-cover"
sizes="48px"
/>
</div>
)
}Custom CDN Loader
Integrate with Cloudinary, Imgix, or any image CDN:
// lib/imageLoader.ts
export default function cloudinaryLoader({
src,
width,
quality,
}: {
src: string
width: number
quality?: number
}) {
const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality ?? 75}`]
return `https://res.cloudinary.com/demo/image/upload/${params.join(',')}/${src}`
}// next.config.js
const nextConfig = {
images: {
loader: 'custom',
loaderFile: './lib/imageLoader.ts',
},
}Common Mistakes
- Using a plain
imgtag instead ofnext/imagefor content images — misses all optimizations - Omitting
widthandheightwithoutfill, causing layout shift (CLS) - Not setting
priorityon the LCP image — it lazy loads and hurts LCP score - Omitting
sizeswhen usingfill, causing mobile devices to download full-width images - Not configuring
remotePatternsfor external hosts — Next.js blocks them by default
Best Practices
- Always use
next/imagefor content images — never plainimg - Set
priorityon exactly one image per page: the hero or first visible image - Provide accurate
sizesvalues to reduce mobile download sizes by up to 3x - Use
placeholder="blur"on large images to improve perceived loading performance - Use
fill+object-coverfor flexible containers instead of fixed width/height - Store static images in
/publicand configureremotePatternsfor external URLs
Key Takeaways
next/imageautomatically converts to WebP/AVIF, reducing file size 30–50% vs JPEGprioritydisables lazy loading and adds a preload link — use it only on the LCP imagefillrequires aposition: relativeparent and must usesizesfor efficiencysizestells the browser display width at each breakpoint — critical for responsive imagesremotePatternsinnext.config.jsmust whitelist every external image hostnameplaceholder="blur"prevents layout shift and improves perceived performance for large imagesqualitydefaults to 75 — increasing it improves sharpness at the cost of file size- Custom loaders enable integration with Cloudinary, Imgix, Contentful, and other CDNs
Advertisement