Introduction to Image Optimization
Images often account for the largest portion of a web page's size and can significantly impact loading performance. Next.js provides a powerful Image component that automatically optimizes images on-demand, reducing file sizes and improving Core Web Vitals scores. The next/image component handles lazy loading, responsive images, modern formats, and more.
Note: The Next.js Image component requires either Sharp (for Node.js runtime) or the built-in image optimization (for Edge Runtime). Sharp is automatically installed when you run npm install, but you may need to install it manually in some environments.
Basic Image Component Usage
Import and use the Image component from next/image:
// app/page.tsx
import Image from 'next/image';
export default function Page() {
return (
<div>
<h1>My Image</h1>
<Image
src="/images/hero.jpg"
alt="Hero image"
width={800}
height={600}
/>
</div>
);
}
The Image component requires three essential props: src, alt, and either width/height or fill. These help Next.js properly optimize and display the image.
Local Images
For local images in the public directory or imported directly:
// Option 1: Public directory
<Image
src="/images/profile.jpg"
alt="Profile picture"
width={400}
height={400}
/>
// Option 2: Import (automatically gets width/height)
import profilePic from './profile.jpg';
<Image
src={profilePic}
alt="Profile picture"
// width and height are automatically provided
/>
Remote Images
For images hosted on external domains, configure allowed domains in next.config.js:
// next.config.js
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'images.unsplash.com',
port: '',
pathname: '/photo-**',
},
{
protocol: 'https',
hostname: 'cdn.example.com',
},
],
},
};
// app/page.tsx
<Image
src="https://images.unsplash.com/photo-1234567890"
alt="Unsplash photo"
width={800}
height={600}
/>
Warning: Always configure remotePatterns for external images to prevent unauthorized domains from using your image optimization service. This protects your server resources and prevents potential abuse.
Image Sizing Strategies
Next.js Image component supports different sizing strategies:
Fixed Size (width and height)
<Image
src="/images/logo.png"
alt="Logo"
width={200}
height={100}
/>
Responsive Size (sizes prop)
Use the sizes prop to specify how much space the image takes at different viewport widths:
<Image
src="/images/hero.jpg"
alt="Hero image"
width={1200}
height={800}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
This tells the browser:
- On screens up to 768px: image is 100% of viewport width
- On screens 769px to 1200px: image is 50% of viewport width
- On screens wider than 1200px: image is 33% of viewport width
Fill Container (fill prop)
Use fill when you don't know the image dimensions or want the image to fill its container:
// CSS
.image-container {
position: relative;
width: 100%;
height: 400px;
}
// Component
<div className="image-container">
<Image
src="/images/banner.jpg"
alt="Banner"
fill
style={{ objectFit: 'cover' }}
/>
</div>
Tip: When using fill, the parent element must have position: relative, position: fixed, or position: absolute. The Image component will automatically fill the entire parent container.
Image Loading Strategies
Control when and how images load:
Lazy Loading (Default)
Images load as they approach the viewport:
<Image
src="/images/product.jpg"
alt="Product"
width={600}
height={400}
loading="lazy" // Default behavior
/>
Eager Loading
Load images immediately (use for above-the-fold content):
<Image
src="/images/hero.jpg"
alt="Hero"
width={1200}
height={600}
loading="eager"
priority // Equivalent to loading="eager"
/>
Priority Images
Mark critical images (like hero images) as priority to preload them:
<Image
src="/images/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority
/>
Note: Use priority sparingly and only for images visible above the fold. Too many priority images can actually harm performance by competing for bandwidth.
Placeholder Strategies
Show placeholders while images load:
Blur Placeholder (Automatic)
For imported images, Next.js automatically generates blur placeholders:
import heroImage from './hero.jpg';
<Image
src={heroImage}
alt="Hero"
placeholder="blur"
// blurDataURL is automatically generated
/>
Custom Blur Placeholder
Provide your own base64-encoded placeholder:
<Image
src="/images/photo.jpg"
alt="Photo"
width={800}
height={600}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD..."
/>
Empty Placeholder
<Image
src="/images/photo.jpg"
alt="Photo"
width={800}
height={600}
placeholder="empty"
/>
Image Quality and Formats
Control image quality and format optimization:
Quality Setting
<Image
src="/images/photo.jpg"
alt="Photo"
width={800}
height={600}
quality={75} // Default is 75, range is 1-100
/>
// High quality for important images
<Image
src="/images/product.jpg"
alt="Product detail"
width={1000}
height={1000}
quality={90}
/>
Format Optimization
Next.js automatically serves modern formats (WebP, AVIF) when supported by the browser. Configure in next.config.js:
// next.config.js
module.exports = {
images: {
formats: ['image/avif', 'image/webp'], // Default
},
};
Responsive Images
Create responsive images that adapt to different screen sizes:
Complete Responsive Example
<Image
src="/images/hero.jpg"
alt="Hero image"
width={1200}
height={675}
sizes="(max-width: 640px) 100vw,
(max-width: 1024px) 80vw,
(max-width: 1280px) 70vw,
60vw"
style={{
width: '100%',
height: 'auto',
}}
priority
/>
Responsive with Fill
// CSS Module
.heroContainer {
position: relative;
width: 100%;
height: 60vh;
min-height: 400px;
}
@media (max-width: 768px) {
.heroContainer {
height: 40vh;
min-height: 300px;
}
}
// Component
<div className={styles.heroContainer}>
<Image
src="/images/hero.jpg"
alt="Hero"
fill
sizes="100vw"
style={{ objectFit: 'cover' }}
priority
/>
</div>
Image Component Props Reference
Complete list of important Image component props:
<Image
// Required props
src="/images/photo.jpg" // string or StaticImageData
alt="Description" // string (important for accessibility)
// Size props (choose one approach)
width={800} // number - explicit width
height={600} // number - explicit height
fill // boolean - fill parent container
// Loading props
loading="lazy" // "lazy" | "eager"
priority // boolean - preload image
// Placeholder props
placeholder="blur" // "blur" | "empty"
blurDataURL="data:..." // string - base64 encoded image
// Quality and optimization
quality={75} // number (1-100)
sizes="100vw" // string - responsive sizes
// Styling
style={{ objectFit: 'cover' }}
className="image-class"
// Event handlers
onLoad={(e) => console.log('Image loaded')}
onError={(e) => console.log('Image error')}
// Other props
unoptimized={false} // boolean - disable optimization
/>
Advanced Configuration
Configure image optimization in next.config.js:
// next.config.js
module.exports = {
images: {
// Device sizes for responsive images
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
// Image sizes (width) for srcset
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
// Formats to use
formats: ['image/avif', 'image/webp'],
// Remote patterns (replaces domains)
remotePatterns: [
{
protocol: 'https',
hostname: '**.example.com',
port: '',
pathname: '/images/**',
},
],
// Minimum cache time in seconds
minimumCacheTTL: 60,
// Disable static imports optimization
disableStaticImages: false,
// Dangerous allow SVG
dangerouslyAllowSVG: true,
contentDispositionType: 'attachment',
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
},
};
Image Gallery Example
Create an optimized image gallery:
// app/gallery/page.tsx
import Image from 'next/image';
const images = [
{ src: '/gallery/1.jpg', alt: 'Image 1' },
{ src: '/gallery/2.jpg', alt: 'Image 2' },
{ src: '/gallery/3.jpg', alt: 'Image 3' },
{ src: '/gallery/4.jpg', alt: 'Image 4' },
];
export default function Gallery() {
return (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{images.map((image, index) => (
<div key={index} className="relative aspect-square">
<Image
src={image.src}
alt={image.alt}
fill
sizes="(max-width: 768px) 50vw, (max-width: 1200px) 33vw, 25vw"
style={{ objectFit: 'cover' }}
className="rounded-lg"
/>
</div>
))}
</div>
);
}
Performance Best Practices
Follow these guidelines for optimal image performance:
1. Use Appropriate Formats
- JPEG for photographs and complex images
- PNG for images requiring transparency
- SVG for icons and logos (inline for small SVGs)
- Let Next.js automatically convert to WebP/AVIF
2. Optimize Image Dimensions
// Bad: Serving 3000x2000 image for 300x200 display
<Image src="/huge-image.jpg" width={300} height={200} />
// Good: Serve appropriately sized image
<Image src="/optimized-image.jpg" width={300} height={200} />
3. Use Priority Wisely
// Good: Priority for above-the-fold hero
<Image src="/hero.jpg" priority />
// Bad: Priority for many images
{products.map(p => (
<Image src={p.image} priority /> // Don't do this!
))}
4. Specify Sizes Accurately
// Matches actual display size at different breakpoints
<Image
src="/responsive.jpg"
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 800px"
/>
5. Use Blur Placeholders
import heroImage from './hero.jpg';
<Image
src={heroImage}
placeholder="blur" // Prevents layout shift
/>
Exercise: Create a responsive product showcase page with:
- A hero image with blur placeholder and priority loading
- A grid of 12 product images with lazy loading
- Responsive sizing using the sizes prop
- Proper alt text for accessibility
- Click to enlarge functionality (bonus)
Ensure proper configuration for remote images if using external CDN sources.
Common Pitfalls and Solutions
Issue: Images not optimizing on production
Solution: Ensure Sharp is installed and you're using a supported hosting platform:
npm install sharp
Issue: Remote images not loading
Solution: Add domain to remotePatterns in next.config.js
Issue: Layout shift when images load
Solution: Always specify width/height or use fill with proper container
Issue: Poor quality images
Solution: Increase quality prop (default is 75, max is 100)
Summary
Next.js Image component provides powerful automatic optimization features. Key takeaways:
- Use next/image for automatic optimization, lazy loading, and responsive images
- Specify width/height or use fill for proper layout
- Configure remotePatterns for external images
- Use priority prop for above-the-fold images
- Leverage blur placeholders to prevent layout shift
- Use sizes prop for responsive images
- Configure quality based on use case (75-90 for most cases)
- Modern formats (WebP, AVIF) are automatically served when supported