How to Optimize Nextjs Images
How to Optimize Next.js Images Image optimization is one of the most critical factors in modern web performance. Slow-loading images can drastically increase page load times, hurt user experience, and negatively impact search engine rankings. In the world of React-based frameworks, Next.js has emerged as a leading choice for building fast, scalable, and SEO-friendly applications. One of its stando
How to Optimize Next.js Images
Image optimization is one of the most critical factors in modern web performance. Slow-loading images can drastically increase page load times, hurt user experience, and negatively impact search engine rankings. In the world of React-based frameworks, Next.js has emerged as a leading choice for building fast, scalable, and SEO-friendly applications. One of its standout features is its built-in Image Component, designed specifically to handle image optimization automatically. However, many developers underutilize or misconfigure this tool, missing out on significant performance gains.
This comprehensive guide walks you through everything you need to know to optimize images in Next.js—from foundational concepts to advanced techniques. Whether you’re building an e-commerce store, a blog, or a corporate website, mastering Next.js image optimization will help you achieve faster load times, better Core Web Vitals scores, and improved SEO performance. By the end of this tutorial, you’ll know how to implement responsive images, leverage modern formats like WebP and AVIF, reduce bundle size, avoid layout shifts, and integrate external image providers with confidence.
Step-by-Step Guide
1. Understand Next.js Image Component Basics
Next.js provides a built-in next/image component that replaces the standard HTML <img> tag. This component automatically optimizes images for performance by handling resizing, lazy loading, format conversion, and caching—all without requiring manual configuration.
To use it, first import the component:
import Image from 'next/image';
Then replace your standard image tags:
<!-- Before -->
<img src="/hero-image.jpg" alt="Hero Banner" />
<!-- After -->
<Image
src="/hero-image.jpg"
alt="Hero Banner"
width={1200}
height={630}
/>
Notice the mandatory width and height props. These are required because Next.js uses them to calculate the aspect ratio and reserve space during rendering, preventing layout shifts (CLS). Always provide accurate dimensions—never rely on CSS alone to define size.
2. Configure Image Optimization in next.config.js
By default, Next.js optimizes images using its internal image optimization API. However, for production deployments, especially when using external image hosts, you must configure allowed domains.
Create or update your next.config.js file:
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
domains: ['example.com', 'cdn.yourdomain.com', 'images.unsplash.com'],
formats: ['image/webp', 'image/avif'],
deviceSizes: [640, 768, 1024, 1280, 1536],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
minimumCacheTTL: 60,
},
};
module.exports = nextConfig;
Let’s break this down:
- domains: Lists all external domains from which you’re loading images. Omitting a domain will cause Next.js to block the image.
- formats: Specifies the output formats. WebP is widely supported; AVIF offers better compression but requires modern browsers.
- deviceSizes: Defines the widths (in pixels) that Next.js will generate for responsive images based on screen size.
- imageSizes: Defines sizes for images that are not full-width (e.g., thumbnails or avatars).
- minimumCacheTTL: Sets the minimum time (in seconds) an optimized image is cached. Increase this for static assets to reduce server load.
Always test your configuration after changes. Use Chrome DevTools’ Network tab to verify that images are being served from the Next.js optimizer (URLs will include /_next/image).
3. Use Local Images Correctly
When using images stored in your project’s /public folder, reference them with a leading slash:
<Image src="/images/logo.png" alt="Logo" width={200} height={50} />
If your images are imported as modules (e.g., from /src/images), use dynamic imports:
import logo from '../src/images/logo.png';
<Image src={logo} alt="Logo" width={200} height={50} />
Importing images as modules allows Next.js to process them during build time, enabling further optimizations like automatic format conversion and inline base64 encoding for very small images.
4. Optimize External Images
Many websites rely on third-party image sources like Unsplash, Cloudinary, or Shopify. To optimize these, you must explicitly allow their domains in next.config.js as shown earlier.
Example with Unsplash:
<Image
src="https://images.unsplash.com/photo-1506744038136-46273834b3fb"
alt="Mountain Landscape"
width={1200}
height={800}
priority
/>
Next.js will fetch the image from Unsplash, optimize it, and serve it from your own CDN. This reduces dependency on third-party servers and improves reliability.
5. Implement Priority Loading for Above-the-Fold Images
Not all images are equally important. The hero banner, logo, or main product image should load immediately. Use the priority prop to instruct Next.js to preload these images:
<Image
src="/hero-banner.jpg"
alt="Main Banner"
width={1920}
height={1080}
priority
/>
This triggers:
- High-priority fetching during server-side rendering (SSR)
- Preloading via
<link rel="preload"> - Early rendering without waiting for JavaScript to hydrate
Use priority sparingly—only on 1–2 critical images per page. Overuse can slow down the initial render by competing for bandwidth.
6. Lazy Load Non-Critical Images
By default, Next.js lazy loads images that are not in the viewport. This behavior is controlled by the loading prop, which defaults to "lazy".
For images below the fold—like gallery thumbnails, blog post images, or product carousels—this is ideal:
<Image
src="/product-thumbnail-1.jpg"
alt="Product 1"
width={300}
height={300}
loading="lazy"
/>
You can also use loading="eager" if you need to override the default (e.g., for images that appear immediately after scroll), but avoid this unless necessary.
7. Use Placeholder and Blur Effects
To improve perceived performance, Next.js supports placeholder images. The placeholder prop accepts either "blur" or "empty".
<Image
src="/large-banner.jpg"
alt="Banner"
width={1200}
height={630}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/..."
/>
When using placeholder="blur", Next.js generates a tiny, low-quality version of the image (typically 20x20px) as a base64 string. This appears immediately while the full image loads, reducing the perception of delay.
For custom blur placeholders, generate a low-res version of your image using tools like TinyPNG or Squoosh, then encode it to base64. You can automate this with scripts or use libraries like next-image-export-optimizer.
8. Serve Modern Image Formats: WebP and AVIF
Modern image formats like WebP and AVIF offer 25–50% smaller file sizes than JPEG or PNG with comparable or better quality. Next.js automatically converts images to these formats when supported by the browser.
To ensure maximum compatibility:
- Enable
formats: ['image/webp', 'image/avif']innext.config.js - Use high-quality source images (preferably PNG or JPEG 90%+ quality)
- Test in Chrome, Edge, Firefox, and Safari to confirm format delivery
AVIF delivers superior compression but has limited support in older browsers. WebP is the safer choice for broad compatibility. Next.js intelligently serves the best format based on the client’s capabilities.
9. Avoid Unnecessary Image Resizing
Next.js generates multiple sizes for each image based on your deviceSizes and imageSizes configuration. However, if you specify a width larger than the original image, Next.js will upscale it—resulting in blurry output.
Always use source images that are at least as large as the maximum display size you intend to use. For example, if your layout displays images up to 1200px wide, use source images that are 1200px or larger.
Tip: Use a consistent naming convention like image-name@2x.jpg or store originals in a /src/assets/originals folder to avoid accidental resizing.
10. Use Static Export with Image Optimization
If you’re using next export to generate a static site, image optimization still works—but only if images are referenced via next/image and hosted locally or via allowed external domains.
External images must be pre-optimized or hosted on a CDN that supports CORS. Next.js cannot optimize external images at build time unless they are fetched during the export process.
To ensure compatibility:
- Pre-optimize all external images using tools like Cloudinary or Imgix
- Use
loader="custom"for advanced control (see next section) - Test your exported site locally with
next export && next start
11. Implement Custom Loaders for Advanced Use Cases
For projects using external image CDNs like Cloudinary, Imgix, or Akamai, you can define a custom loader to bypass Next.js’s internal optimizer and use the CDN’s native optimization features.
Example with Cloudinary:
const imageLoader = ({ src, width, quality }) => {
return https://res.cloudinary.com/your-cloud-name/image/upload/w_${width},q_${quality || 75}/${src};
};
<Image
loader={imageLoader}
src="my-image.jpg"
alt="Custom Loader Example"
width={1200}
height={800}
/>
Now, Next.js will generate URLs like:
https://res.cloudinary.com/your-cloud-name/image/upload/w_1200,q_75/my-image.jpg
This approach gives you full control over optimization parameters (crop, quality, format, etc.) while still benefiting from Next.js’s automatic sizing and lazy loading.
For global use, define the loader in next.config.js:
const nextConfig = {
images: {
loader: 'custom',
path: 'https://res.cloudinary.com/your-cloud-name/image/upload/',
domains: ['res.cloudinary.com'],
},
};
Then simply use src without the full URL:
<Image src="my-image.jpg" width={1200} height={800} />
12. Monitor Performance with Lighthouse and Web Vitals
After implementing image optimizations, validate results using Chrome DevTools’ Lighthouse report. Look for:
- “Properly size images” – should show 0 issues
- “Efficiently encode images” – should show minimal or no recommendations
- “Defer offscreen images” – should be satisfied by lazy loading
- “Serve images in next-gen formats” – should show WebP/AVIF usage
Also integrate Web Vitals monitoring via next/web-vitals to track real-user metrics:
// pages/_app.js
import { reportWebVitals } from 'next/web-vitals';
const reportWebVitals = (metric) => {
console.log(metric);
};
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />;
}
export default reportWebVitals;
Send these metrics to Google Analytics, Data Studio, or a custom backend to monitor long-term performance trends.
Best Practices
1. Always Specify Width and Height
Never omit width and height props. Without them, Next.js cannot calculate the aspect ratio, leading to layout shifts (CLS), which hurt Core Web Vitals scores and user experience.
Even if you’re using CSS to scale the image, define the original dimensions of the source file. For example, if your image is 1920x1080, use those values—even if you display it at 800px wide.
2. Use Appropriate Image Formats
Choose the right format for the content:
- WebP: Best for photos and complex images. 30% smaller than JPEG.
- AVIF: Best for high-detail images. Up to 50% smaller than JPEG. Use if targeting modern browsers.
- PNG: Use only for graphics with transparency or sharp edges (logos, icons).
- JPEG: Legacy format. Avoid unless required for compatibility.
Convert all JPEG/PNG files to WebP during your build process using tools like sharp or imagemin.
3. Compress Source Images Before Upload
Even with Next.js optimization, starting with a 5MB image is inefficient. Compress source files to 1–2MB max before adding them to your project.
Use tools like:
- Squoosh (web-based, free)
- ImageOptim (Mac)
- ShortPixel (online, bulk)
- Photoshop Save for Web
Target 70–85% quality for photos. Lower for thumbnails.
4. Avoid Using Background Images with Next.js Image
The next/image component is designed for <img> elements, not CSS background images. If you need background images, use standard CSS with optimized, compressed assets.
For hero sections with background images:
const Hero = () => (
<div style={{
backgroundImage: 'url(/hero-bg.webp)',
backgroundSize: 'cover',
height: '100vh',
}}>
<Image src="/hero-text-overlay.png" alt="Overlay" width={800} height={300} />
</div>
);
Ensure the background image is pre-optimized and served in WebP format via your build pipeline.
5. Use Responsive Breakpoints Wisely
Next.js generates images at sizes defined in deviceSizes and imageSizes. Don’t overload these arrays. Stick to common breakpoints:
- 640px (mobile)
- 768px (tablet)
- 1024px (small desktop)
- 1280px (standard desktop)
- 1536px (large desktop)
More sizes = more server load and disk usage. Only add sizes you actually use in your layout.
6. Cache Images Aggressively
Set a high minimumCacheTTL in next.config.js (e.g., 3600 seconds or more). This reduces redundant processing and improves CDN efficiency.
Also ensure your hosting provider (Vercel, Netlify, etc.) caches optimized images at the edge. Vercel does this automatically.
7. Don’t Use Inline Base64 for Large Images
While base64 placeholders are useful for blur effects, avoid embedding entire images as base64 in your code. This bloats your JavaScript bundles and slows down hydration.
Only use base64 for tiny images under 1KB (e.g., icons or loading spinners).
8. Optimize for Mobile First
Mobile users often have slower connections. Ensure your mobile breakpoints serve appropriately sized images. Use loading="lazy" on all non-critical images below the fold.
Test on throttled networks (3G in DevTools) to simulate real-world conditions.
9. Remove Unused Images
Over time, projects accumulate unused image files. Use tools like next-images-optimizer or custom scripts to detect and delete orphaned assets.
Also, avoid importing the same image multiple times in different components. Use a shared image module or constants file.
10. Combine with CDN and SSR
For maximum performance, deploy your Next.js app on Vercel or another platform that provides global CDN and edge caching. Optimized images are automatically served from the nearest edge location.
Combine with Server-Side Rendering (SSR) or Static Site Generation (SSG) to ensure images are optimized and delivered before client-side hydration.
Tools and Resources
1. Next.js Image Documentation
The official documentation is the most authoritative source: https://nextjs.org/docs/app/building-your-application/optimizing/images
2. Squoosh
A free, open-source web app by Google for compressing and converting images. Supports WebP, AVIF, JPEG XL, and more.
3. Sharp
A high-performance Node.js library for image processing. Use it to automate bulk conversion of images to WebP/AVIF during build.
https://sharp.pixelplumbing.com
4. Cloudinary
A cloud-based image and video management platform with automatic optimization, transformation, and delivery. Integrates seamlessly with Next.js via custom loaders.
5. Imgix
Real-time image processing CDN. Offers advanced features like smart cropping, face detection, and format auto-selection.
6. WebP Converter (CLI)
A command-line tool to batch convert JPEG/PNG to WebP:
npm install -g webp-converter
webp-converter -i ./src/images -o ./public/images/webp -q 80
7. Lighthouse
Chrome DevTools extension for auditing performance, accessibility, SEO, and more. Use it to validate image optimization results.
8. ImageOptim (Mac)
Free Mac app that strips metadata and compresses PNG, JPEG, and GIF files without quality loss.
9. TinyPNG / TinyJPG
Online service that uses smart lossy compression to reduce PNG and JPEG sizes by up to 80%.
10. Next-Image-Export-Optimizer
A plugin that optimizes and converts images during static export. Useful for SSG sites with large image libraries.
https://github.com/iamdustan/next-image-export-optimizer
Real Examples
Example 1: E-Commerce Product Page
Scenario: A product page with a main image, 4 thumbnails, and a hero banner.
import Image from 'next/image';
const ProductPage = () => (
<div>
<!-- Hero Banner -->
<Image
src="/products/hero/phone-1.jpg"
alt="iPhone 15 Pro"
width={1920}
height={1080}
priority
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/..."
/>
<!-- Main Product Image -->
<Image
src="/products/main/iphone-15-pro-black.jpg"
alt="iPhone 15 Pro Black"
width={800}
height={800}
className="mt-8"
/>
<!-- Thumbnail Gallery -->
<div className="flex space-x-4 mt-6">
<Image
src="/products/thumbs/iphone-15-pro-black.jpg"
alt="Black"
width={100}
height={100}
loading="lazy"
/>
<Image
src="/products/thumbs/iphone-15-pro-blue.jpg"
alt="Blue"
width={100}
height={100}
loading="lazy"
/>
<Image
src="/products/thumbs/iphone-15-pro-silver.jpg"
alt="Silver"
width={100}
height={100}
loading="lazy"
/>
<Image
src="/products/thumbs/iphone-15-pro-titanium.jpg"
alt="Titanium"
width={100}
height={100}
loading="lazy"
/>
</div>
</div>
);
Optimization results:
- Hero image: Preloaded, served as WebP, 450KB → 180KB
- Main image: Lazy-loaded, converted to WebP, 600KB → 220KB
- Thumbnails: 100x100px, served as AVIF, 80KB → 35KB each
- CLS score improved from 0.15 to 0.01
- FCP reduced by 1.2 seconds
Example 2: Blog with User-Uploaded Images
Scenario: A CMS-powered blog where users upload JPEG images. You want to optimize them automatically.
Use a custom loader with Cloudinary:
// next.config.js
const nextConfig = {
images: {
loader: 'custom',
path: 'https://res.cloudinary.com/myblog/image/upload/',
formats: ['image/webp', 'image/avif'],
},
};
// BlogPost.js
import Image from 'next/image';
const BlogPost = ({ post }) => (
<article>
<h1>{post.title}</h1>
<Image
src={post.featuredImage} // e.g., "uploads/blog1.jpg"
alt={post.title}
width={1200}
height={630}
priority
/>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
When a user uploads uploads/blog1.jpg, Cloudinary automatically:
- Converts to WebP/AVIF
- Resizes for device breakpoints
- Applies smart compression
- Serves from global CDN
Result: 90% reduction in image bandwidth, faster page loads, and zero manual optimization required.
Example 3: Static Site with 500+ Images
Scenario: A portfolio website with hundreds of project screenshots.
Challenge: Static export takes too long due to image processing.
Solution:
- Pre-optimize all images to WebP using Sharp during build
- Store optimized images in
/public/images/optimized - Use
next/imagewith local paths - Set
minimumCacheTTL: 31536000(1 year)
// build script: build-images.js
const sharp = require('sharp');
const fs = require('fs');
const path = require('path');
const sourceDir = './src/images';
const destDir = './public/images/optimized';
fs.readdirSync(sourceDir).forEach(file => {
const srcPath = path.join(sourceDir, file);
const destPath = path.join(destDir, file.replace(/\.(jpg|jpeg|png)$/, '.webp'));
sharp(srcPath)
.webp({ quality: 80 })
.toFile(destPath);
});
Then in components:
<Image src="/images/optimized/project-1.webp" width={800} height={600} />
Result: Build time reduced from 12 minutes to 45 seconds. All images served at optimal size and format.
FAQs
Do I need to install any packages to use Next.js Image?
No. The next/image component is built into Next.js. No additional dependencies are required.
Why are my images still loading as JPEG even though I set WebP?
Check your browser. If you’re using Safari 15 or older, it doesn’t support WebP. Next.js will fall back to the original format. Test in Chrome or Edge to confirm WebP delivery.
Can I use next/image with dynamic image URLs from an API?
Yes, but you must include the domain in next.config.js under images.domains. For example, if your API returns https://api.example.com/image.jpg, add 'api.example.com' to the domains list.
What happens if I don’t provide width and height?
Next.js will throw a build-time error in development. In production, it may render incorrectly or cause layout shifts, hurting your Core Web Vitals.
Can I optimize SVG files with next/image?
No. The next/image component does not optimize SVGs. Use standard <img> or inline SVGs instead. SVGs are vector-based and don’t benefit from pixel-based optimization.
How does image optimization affect SEO?
Optimized images improve page speed, which is a direct Google ranking factor. Faster pages reduce bounce rates and increase dwell time—indirect SEO signals. Also, properly sized images with descriptive alt text improve accessibility and image search visibility.
Is image optimization available in Next.js App Router?
Yes. The next/image component works identically in both Pages Router and App Router. The configuration in next.config.js applies globally.
Can I disable image optimization?
Yes. Set unoptimized: true in the Image component:
<Image src="/image.jpg" alt="..." width={500} height={300} unoptimized />
Use this only if you’re handling optimization externally (e.g., via a CDN).
Why does my image look blurry after optimization?
This usually happens when the source image is smaller than the specified width. Always use source images that are at least as large as the largest display size you intend to use.
How do I test if images are being optimized?
Open Chrome DevTools → Network tab → Filter by “Img”. Look for URLs containing /_next/image. The response headers should include content-type: image/webp or image/avif.
Conclusion
Optimizing images in Next.js is not optional—it’s essential for delivering fast, modern, and SEO-friendly web experiences. The built-in next/image component is one of the most powerful tools in the Next.js ecosystem, but its full potential is only unlocked through proper configuration and disciplined implementation.
By following the practices outlined in this guide—specifying accurate dimensions, enabling modern formats, using priority and lazy loading, integrating external CDNs, and monitoring performance—you can dramatically reduce image-related load times and improve Core Web Vitals scores.
Remember: image optimization is not a one-time task. As your site grows, so does your image library. Automate conversion, enforce size standards, and regularly audit performance with Lighthouse and Web Vitals.
When images load instantly, users stay longer. When pages load fast, search engines rank higher. And when both happen, your business thrives.
Start optimizing today. Your users—and your SEO—will thank you.