Next.js Image Optimization
Overview
Next.js provides powerful image optimization features through its built-in Image component, automatically optimizing images to improve website performance and user experience.
🎯 Why Image Optimization?
Impact of Images on Performance
javascript
// Problems with traditional HTML image tags
<img src="/large-image.jpg" alt="Large image" />
// ❌ No automatic optimization
// ❌ No responsive sizing
// ❌ May cause layout shift
// ❌ Slow loadingAdvantages of Next.js Image Component
- Automatic Optimization: Automatically converts to modern formats (WebP, AVIF)
- Responsive: Automatically adjusts size based on device
- Lazy Loading: Delays loading images outside viewport
- Prevents Layout Shift: Reserves space to avoid CLS
- On-Demand Optimization: Optimizes on first request, then caches
📦 Image Component Basics
Basic Usage
jsx
import Image from 'next/image';
export default function ProfilePage() {
return (
<div>
<h1>User Profile</h1>
{/* Local image */}
<Image
src="/profile.jpg"
alt="User avatar"
width={500}
height={500}
/>
{/* Remote image */}
<Image
src="https://example.com/photo.jpg"
alt="Photo"
width={800}
height={600}
/>
</div>
);
}Required Properties
jsx
function ImageExample() {
return (
<Image
src="/image.jpg" // Image path (required)
alt="Description" // Alt text (required)
width={800} // Width (required, unless fill)
height={600} // Height (required, unless fill)
/>
);
}🖼️ Local Images
Importing Local Images
jsx
import Image from 'next/image';
import profilePic from '../public/profile.jpg';
export default function Profile() {
return (
<div>
{/* Automatically gets width and height */}
<Image
src={profilePic}
alt="User avatar"
placeholder="blur" // Automatic blur placeholder
/>
</div>
);
}Advantages of Static Imports
jsx
import heroImage from '@/public/hero.jpg';
// ✅ Automatically gets dimensions
// ✅ Automatically generates blur placeholder
// ✅ Prevents layout shift
export default function Hero() {
return (
<Image
src={heroImage}
alt="Hero image"
priority // Priority loading
/>
);
}🌐 Remote Images
Configuring Remote Image Domains
javascript
// next.config.js
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
port: '',
pathname: '/images/**',
},
{
protocol: 'https',
hostname: 'cdn.example.com',
},
],
},
};Using Remote Images
jsx
import Image from 'next/image';
export default function Gallery() {
const images = [
'https://example.com/images/photo1.jpg',
'https://example.com/images/photo2.jpg',
'https://example.com/images/photo3.jpg',
];
return (
<div className="gallery">
{images.map((src, index) => (
<Image
key={index}
src={src}
alt={`Photo ${index + 1}`}
width={400}
height={300}
/>
))}
</div>
);
}🎨 Responsive Images
Using fill Property
jsx
import Image from 'next/image';
export default function ResponsiveImage() {
return (
<div style={{ position: 'relative', width: '100%', height: '400px' }}>
<Image
src="/banner.jpg"
alt="Banner"
fill
style={{ objectFit: 'cover' }}
/>
</div>
);
}Optimizing with sizes Property
jsx
export default function ResponsiveGallery() {
return (
<div className="container">
<Image
src="/photo.jpg"
alt="Photo"
width={800}
height={600}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
</div>
);
}⚡ Performance Optimization
Priority Loading for Critical Images
jsx
export default function HomePage() {
return (
<div>
{/* Above-the-fold image - priority loading */}
<Image
src="/hero.jpg"
alt="Hero image"
width={1200}
height={600}
priority
/>
{/* Other images - lazy loading */}
<Image
src="/content.jpg"
alt="Content image"
width={800}
height={400}
/>
</div>
);
}Placeholder Strategies
jsx
import Image from 'next/image';
export default function PlaceholderExamples() {
return (
<div>
{/* Blur placeholder */}
<Image
src="/photo1.jpg"
alt="Photo 1"
width={500}
height={500}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRg..."
/>
{/* Empty placeholder */}
<Image
src="/photo2.jpg"
alt="Photo 2"
width={500}
height={500}
placeholder="empty"
/>
</div>
);
}Quality Control
jsx
export default function QualityControl() {
return (
<div>
{/* High quality (default 75) */}
<Image
src="/high-quality.jpg"
alt="High quality"
width={800}
height={600}
quality={90}
/>
{/* Thumbnail - lower quality */}
<Image
src="/thumbnail.jpg"
alt="Thumbnail"
width={200}
height={150}
quality={60}
/>
</div>
);
}🔧 Advanced Configuration
Custom Loader
javascript
// next.config.js
module.exports = {
images: {
loader: 'custom',
loaderFile: './my-loader.js',
},
};javascript
// my-loader.js
export default function myImageLoader({ src, width, quality }) {
return `https://cdn.example.com/${src}?w=${width}&q=${quality || 75}`;
}Image Format Configuration
javascript
// next.config.js
module.exports = {
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
},
};🎯 Practical Examples
Image Gallery
jsx
'use client';
import { useState } from 'react';
import Image from 'next/image';
export default function ImageGallery() {
const [selectedImage, setSelectedImage] = useState(null);
const images = [
{ id: 1, src: '/gallery/1.jpg', alt: 'Image 1' },
{ id: 2, src: '/gallery/2.jpg', alt: 'Image 2' },
{ id: 3, src: '/gallery/3.jpg', alt: 'Image 3' },
];
return (
<div>
<div className="grid grid-cols-3 gap-4">
{images.map((image) => (
<div
key={image.id}
className="cursor-pointer"
onClick={() => setSelectedImage(image)}
>
<Image
src={image.src}
alt={image.alt}
width={300}
height={200}
className="rounded-lg hover:opacity-80"
/>
</div>
))}
</div>
{selectedImage && (
<div className="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center">
<div className="relative w-full max-w-4xl">
<Image
src={selectedImage.src}
alt={selectedImage.alt}
width={1200}
height={800}
className="rounded-lg"
/>
<button
onClick={() => setSelectedImage(null)}
className="absolute top-4 right-4 text-white"
>
Close
</button>
</div>
</div>
)}
</div>
);
}Background Image
jsx
import Image from 'next/image';
export default function HeroSection() {
return (
<div className="relative h-screen">
<Image
src="/hero-bg.jpg"
alt="Background"
fill
style={{ objectFit: 'cover' }}
priority
/>
<div className="relative z-10 flex items-center justify-center h-full">
<h1 className="text-white text-6xl font-bold">
Welcome to Our Website
</h1>
</div>
</div>
);
}📝 Best Practices
1. Always Provide alt Text
jsx
// ✅ Good practice
<Image src="/photo.jpg" alt="Beach at sunset" width={800} height={600} />
// ❌ Bad practice
<Image src="/photo.jpg" alt="" width={800} height={600} />2. Set priority for Above-the-Fold Images
jsx
export default function HomePage() {
return (
<div>
<Image
src="/hero.jpg"
alt="Hero image"
width={1200}
height={600}
priority // Above-the-fold image
/>
</div>
);
}3. Use Appropriate Sizes
jsx
// ✅ Set based on actual display size
<Image src="/thumb.jpg" alt="Thumbnail" width={200} height={150} />
// ❌ Avoid oversized dimensions
<Image src="/thumb.jpg" alt="Thumbnail" width={2000} height={1500} />4. Configure Remote Image Domains
javascript
// next.config.js
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: '**.example.com', // Supports subdomains
},
],
},
};📊 Performance Monitoring
Using Next.js Analytics
jsx
// app/layout.js
import { Analytics } from '@vercel/analytics/react';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
);
}📝 Chapter Summary
Next.js image optimization provides powerful performance capabilities through the Image component, including automatic format conversion, responsive sizing, lazy loading, and more.
Key Takeaways
- ✅ Use Image component instead of img tag
- ✅ Configure domain whitelist for remote images
- ✅ Use priority attribute for above-the-fold images
- ✅ Use fill attribute for responsive layouts
- ✅ Provide meaningful alt text
- ✅ Adjust quality parameters based on actual needs
Next Steps
In the next chapter, we'll learn about Next.js font optimization.
Continue Learning: Next Chapter - Next.js Font Optimization