Next.js Image Optimization
Optimize images in Next.js using the Image component for better performance.
TL;DR
- 01Use next/image instead of img tags for automatic optimization.
- 02Lazy loading and responsive resizing happen automatically.
- 03Specify width and height to prevent layout shift and improve scores.
Tips
- 01Always specify width and height — it prevents layout shift and improves performance significantly.
Warnings
- 01Don't use Image for images that change dimensions frequently — use regular img tags instead.
Basic Image Component
- Import and use Image component from next/image.
import Image from 'next/image'; export default function Hero() { return ( <Image src="/hero.jpg" alt="Hero image" width={1200} height={600} /> ); } - Always specify width and height for best performance.
- alt text is required for accessibility.
- Images are served as WebP automatically for supported browsers.
- Use the placeholder prop to show a blur while the image loads.
import Image from 'next/image'; import heroImg from '@/public/hero.jpg'; <Image src={heroImg} alt="Hero" placeholder="blur" // works with static imports automatically /> - Use blurDataURL to provide a custom blur placeholder for external images.
<Image src="https://cdn.example.com/photo.jpg" alt="Photo" width={800} height={600} placeholder="blur" blurDataURL="data:image/png;base64,..." />
Local vs External Images
- Local images in public folder.
import Image from 'next/image'; import heroImage from '@/public/hero.jpg'; <Image src={heroImage} alt="Hero" /> - Use remotePatterns to allowlist external image domains.
// next.config.js module.exports = { images: { remotePatterns: [{ hostname: "cdn.example.com", pathname: "/uploads/**" }] } }; - Match multiple domains with separate remotePatterns entries.
module.exports = { images: { remotePatterns: [ { hostname: "cdn.example.com" }, { hostname: "images.example.com", protocol: "https" } ] } }; - Static imports infer width, height, and enable blur placeholder automatically.
import logo from '@/public/logo.png'; // width/height inferred <Image src={logo} alt="Logo" /> - Use unoptimized to skip optimization for SVGs or animated GIFs.
<Image src="/icon.svg" alt="Icon" width={32} height={32} unoptimized />
Responsive Images
- Use fill prop for responsive sizing.
<Image src="/hero.jpg" alt="Hero" fill style={{ objectFit: 'cover' }} /> - Specify sizes for responsive images.
<Image src="/card.jpg" alt="Card" width={300} height={200} sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw" /> - The fill image must be inside a positioned container.
<div style={{ position: "relative", width: "100%", height: "400px" }}> <Image src="/banner.jpg" alt="Banner" fill style={{ objectFit: "cover" }} /> </div> - Use objectPosition to control the focal point of a filled image.
<Image src="/portrait.jpg" alt="Portrait" fill style={{ objectFit: "cover", objectPosition: "top center" }} /> - Use sizes to help the browser choose the right srcset variant.
<Image src="/hero.jpg" alt="Hero" fill sizes="100vw" // full-width image />
Lazy Loading
- Lazy loading is automatic by default.
<Image src="/below-fold.jpg" alt="Below fold image" width={800} height={600} loading="lazy" // Default behavior /> - Disable lazy loading for above-fold images.
<Image src="/hero.jpg" alt="Hero" width={1200} height={600} priority // Load immediately /> - Use priority on the largest above-fold image to improve LCP score.
- Eager loading loads the image immediately without waiting for viewport.
<Image src="/logo.png" alt="Logo" width={120} height={40} loading="eager" // Don't defer /> - Images outside the viewport are deferred until the user scrolls near them.
<Image src="/infographic.png" alt="Infographic" width={800} height={600} // loading="lazy" is the default — no need to set it explicitly />
Image Quality
- Control image quality with quality prop.
<Image src="/photo.jpg" alt="Photo" width={500} height={300} quality={80} // 1-100, default 75 /> - Format automatically optimized for browser.
// Automatically serves WebP to browsers that support it <Image src="/photo.jpg" alt="Photo" width={500} height={300} /> - Lower quality to 60–70 for hero images that cover large areas.
<Image src="/hero.jpg" alt="Hero" fill quality={65} /> // Good visual quality at smaller file sizes for large backgrounds - Configure global image quality defaults in next.config.js.
module.exports = { images: { qualities: [75, 90], // only generate these quality variants minimumCacheTTL: 86400 // cache optimized images for 1 day } }; - Use quality={100} for images where accuracy matters like product photos.
<Image src="/product-detail.png" alt="Product" width={600} height={600} quality={100} />
FAQ
Yes — add the external hostname to the remotePatterns array in next.config.js under the images key. Without this, Next.js will block external image requests for security reasons.
Use the fill prop on the Image component and set the parent element to position: relative with a defined size. Then use sizes prop to hint the browser about the rendered width at different breakpoints for optimal file delivery.
Missing or incorrect sizes prop is the most common culprit — without it, Next.js serves a full-resolution image regardless of display size. Define sizes to match your layout breakpoints so the right image size is requested.
The default quality is 75, which balances file size and visual fidelity for most cases. Pass quality={90} or higher only for images where compression artifacts are noticeable, such as product photos or hero images.
Add the priority prop to images that appear above the fold — like hero banners or LCP candidates — so they are eagerly fetched instead of deferred. Only use it for the first few visible images; overusing it defeats the performance benefit of lazy loading.