Images are often the largest assets on web pages, causing slow loading and poor performance. The Next.js Image component solves this with automatic optimization—resizing images on-demand, converting to modern formats (WebP, AVIF), lazy loading, and preventing layout shift. You get dramatically better performance with zero configuration. Let's master optimized images in Next.js!
Why Use the Image Component?
❌ Regular <img> Tag
<img
src="/hero.jpg"
alt="Hero image"
width="1200"
height="600"
/>
<!-- Problems: -->
<!-- ❌ No optimization (large file sizes) -->
<!-- ❌ No format conversion (no WebP/AVIF) -->
<!-- ❌ No lazy loading (loads immediately) -->
<!-- ❌ Layout shift (image pops in) -->
<!-- ❌ No responsive sizing -->
<!-- ❌ Poor performance -->✅ Next.js Image Component
import Image from 'next/image';
<Image
src="/hero.jpg"
alt="Hero image"
width={1200}
height={600}
/>
// Benefits:
// ✅ Automatic optimization
// ✅ Modern formats (WebP, AVIF)
// ✅ Lazy loading by default
// ✅ No layout shift
// ✅ Responsive sizing
// ✅ Dramatically fasterPerformance Benefits
- Smaller files: Images automatically resized and compressed
- Modern formats: Converts to WebP/AVIF (30-80% smaller)
- Lazy loading: Images load only when entering viewport
- No layout shift: Reserved space prevents content jumping
- Responsive: Serves appropriate size for each device
- On-demand: Optimizes images when requested, not at build time
Basic Image Component Usage
Static Image (Local File)
import Image from 'next/image';
export default function Home() {
return (
<div>
<Image
src="/images/hero.jpg"
alt="Hero image"
width={1200}
height={600}
/>
</div>
);
}
// ✅ Image in /public/images/hero.jpg
// ✅ Referenced with /images/hero.jpg
// ✅ Width and height prevent layout shift
// ✅ Automatically optimizedRequired Props
<Image
src="/images/product.jpg" // Required: Image path
alt="Product image" // Required: Accessibility
width={800} // Required*: Width in pixels
height={600} // Required*: Height in pixels
/>
// * width and height required UNLESS using fill prop
// ✅ src: Path to image
// ✅ alt: Description for screen readers and SEO
// ✅ width/height: Prevents layout shift, enables optimization⚠️ Always Provide Alt Text
The alt prop is required for accessibility. Describe what's in the image for screen readers and users who can't see images. It also helps SEO!
Image Sizing Patterns
Pattern 1: Fixed Dimensions
import Image from 'next/image';
export function ProductCard() {
return (
<div>
{/* Fixed size image */}
<Image
src="/images/product.jpg"
alt="Product"
width={400}
height={400}
/>
</div>
);
}
// ✅ Use for images with known dimensions
// ✅ Width and height in pixels
// ✅ Aspect ratio maintained
// ✅ Prevents layout shiftPattern 2: Fill Container (Responsive)
import Image from 'next/image';
export function Hero() {
return (
<div className="relative w-full h-96">
{/* Image fills parent container */}
<Image
src="/images/hero.jpg"
alt="Hero"
fill
className="object-cover"
/>
</div>
);
}
// ✅ fill prop makes image fill parent
// ✅ Parent must have position: relative
// ✅ Use object-fit for sizing behavior:
// - object-cover: fills container, crops if needed
// - object-contain: fits inside, maintains aspect ratio
// - object-fill: stretches to fill
// Common use cases:
// - Hero sections
// - Background images
// - Card images
// - Responsive galleriesPattern 3: Responsive with max-width
import Image from 'next/image';
export function BlogImage() {
return (
<div className="relative w-full max-w-3xl mx-auto">
{/* Responsive image with max-width */}
<Image
src="/images/article.jpg"
alt="Article illustration"
width={1200}
height={675}
className="w-full h-auto"
/>
</div>
);
}
// ✅ width/height for aspect ratio
// ✅ className="w-full h-auto" makes it responsive
// ✅ Container sets max-width
// ✅ Maintains aspect ratioLoading and Performance
Lazy Loading (Default)
import Image from 'next/image';
export function Gallery() {
return (
<div className="grid grid-cols-3 gap-4">
{/* All images lazy load by default */}
<Image src="/images/img1.jpg" alt="Image 1" width={400} height={300} />
<Image src="/images/img2.jpg" alt="Image 2" width={400} height={300} />
<Image src="/images/img3.jpg" alt="Image 3" width={400} height={300} />
</div>
);
}
// ✅ Lazy loading is default
// ✅ Images load when entering viewport
// ✅ Saves bandwidth
// ✅ Faster initial page loadPriority Loading (Above the Fold)
import Image from 'next/image';
export function Hero() {
return (
<div className="relative w-full h-screen">
{/* Priority image - loads immediately */}
<Image
src="/images/hero.jpg"
alt="Hero"
fill
priority
className="object-cover"
/>
</div>
);
}
// ✅ priority prop disables lazy loading
// ✅ Image preloaded for faster LCP
// ✅ Use for above-the-fold images only
// ✅ Improves Core Web Vitals (LCP)
// When to use priority:
// ✅ Hero images
// ✅ Logo
// ✅ Above-the-fold content
// ❌ Below-the-fold images (use lazy loading)Loading States
import Image from 'next/image';
export function ProductImage() {
return (
<Image
src="/images/product.jpg"
alt="Product"
width={800}
height={600}
loading="lazy" // 'lazy' (default) or 'eager'
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRg..." // Base64 blur
/>
);
}
// loading prop:
// - 'lazy': Lazy load (default)
// - 'eager': Load immediately
// placeholder prop:
// - 'empty': No placeholder (default)
// - 'blur': Show blur until loaded
// ✅ Blur placeholder improves perceived performance
// ✅ User sees something while image loadsStyling Images
Using className
import Image from 'next/image';
export function StyledImage() {
return (
<Image
src="/images/avatar.jpg"
alt="Avatar"
width={100}
height={100}
className="rounded-full border-4 border-white shadow-lg"
/>
);
}
// ✅ Use className for Tailwind or CSS
// ✅ Styles applied to <img> element
// ✅ Works with Tailwind, CSS Modules, etc.Object Fit with Fill
import Image from 'next/image';
// Cover - fills container, crops if needed
export function CoverImage() {
return (
<div className="relative w-full h-64">
<Image
src="/images/hero.jpg"
alt="Hero"
fill
className="object-cover"
/>
</div>
);
}
// Contain - fits inside, maintains aspect ratio
export function ContainImage() {
return (
<div className="relative w-full h-64 bg-gray-100">
<Image
src="/images/logo.png"
alt="Logo"
fill
className="object-contain p-4"
/>
</div>
);
}
// object-cover: Fills, crops excess
// object-contain: Fits inside, shows all
// object-fill: Stretches to fill
// object-none: Original size
// object-scale-down: Smaller of contain or noneRounded Images
import Image from 'next/image';
// Circle avatar
export function Avatar() {
return (
<Image
src="/images/user.jpg"
alt="User"
width={80}
height={80}
className="rounded-full"
/>
);
}
// Rounded corners
export function CardImage() {
return (
<Image
src="/images/card.jpg"
alt="Card"
width={400}
height={300}
className="rounded-lg"
/>
);
}
// ✅ rounded-full: Circle
// ✅ rounded-lg: Rounded corners
// ✅ Works with any CSSRemote Images
Configure Remote Domains
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
port: '',
pathname: '/images/**',
},
{
protocol: 'https',
hostname: 'cdn.example.com',
},
],
},
};
module.exports = nextConfig;
// ✅ Whitelist external image domains
// ✅ Security - prevents unauthorized sources
// ✅ Use remotePatterns (not deprecated domains)Using Remote Images
import Image from 'next/image';
export default function Page() {
return (
<div>
{/* Remote image from whitelisted domain */}
<Image
src="https://example.com/images/photo.jpg"
alt="Remote photo"
width={800}
height={600}
/>
{/* From CDN */}
<Image
src="https://cdn.example.com/avatar.jpg"
alt="User avatar"
width={100}
height={100}
className="rounded-full"
/>
</div>
);
}
// ✅ Remote images optimized like local images
// ✅ Must configure domain in next.config.js
// ✅ Works with CDNs, CMSs, etc.Dynamic Remote Images
import Image from 'next/image';
interface Product {
id: string;
name: string;
imageUrl: string;
}
export default async function ProductPage({
params,
}: {
params: { id: string };
}) {
const product: Product = await fetch(
`https://api.example.com/products/${params.id}`
).then(r => r.json());
return (
<div>
<h1>{product.name}</h1>
{/* Dynamic remote image */}
<Image
src={product.imageUrl}
alt={product.name}
width={800}
height={600}
/>
</div>
);
}
// ✅ Image URL from API
// ✅ Optimized like any other image
// ✅ Domain must be whitelistedPractical Examples
Example 1: Hero Section
import Image from 'next/image';
export function Hero() {
return (
<section className="relative h-screen w-full">
{/* Background image */}
<Image
src="/images/hero-bg.jpg"
alt="Hero background"
fill
priority
className="object-cover"
/>
{/* Overlay */}
<div className="absolute inset-0 bg-black/50" />
{/* Content */}
<div className="relative z-10 h-full flex items-center justify-center text-white">
<div className="text-center">
<h1 className="text-6xl font-bold mb-4">
Welcome to Our Platform
</h1>
<p className="text-xl mb-8">
Build amazing things with Next.js
</p>
<button className="px-8 py-4 bg-blue-600 rounded-lg font-bold">
Get Started
</button>
</div>
</div>
</section>
);
}
// ✅ Full-screen background image
// ✅ priority for above-the-fold
// ✅ object-cover to fill
// ✅ Overlay for readabilityExample 2: Product Grid
import Image from 'next/image';
interface Product {
id: string;
name: string;
price: number;
image: string;
}
export function ProductGrid({ products }: { products: Product[] }) {
return (
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-6">
{products.map(product => (
<div key={product.id} className="bg-white rounded-lg shadow-lg overflow-hidden">
{/* Product image */}
<div className="relative w-full h-64">
<Image
src={product.image}
alt={product.name}
fill
className="object-cover hover:scale-105 transition-transform duration-300"
/>
</div>
{/* Product info */}
<div className="p-4">
<h3 className="font-bold text-lg mb-2">{product.name}</h3>
<p className="text-2xl font-bold text-blue-600">
${product.price}
</p>
<button className="w-full mt-4 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
Add to Cart
</button>
</div>
</div>
))}
</div>
);
}
// ✅ Responsive grid
// ✅ fill for card images
// ✅ Lazy loading (default)
// ✅ Hover zoom effectExample 3: Avatar Component
import Image from 'next/image';
interface AvatarProps {
src: string;
alt: string;
size?: 'sm' | 'md' | 'lg';
}
export function Avatar({ src, alt, size = 'md' }: AvatarProps) {
const sizes = {
sm: 40,
md: 80,
lg: 120,
};
const dimension = sizes[size];
return (
<Image
src={src}
alt={alt}
width={dimension}
height={dimension}
className="rounded-full border-2 border-white shadow-lg"
/>
);
}
// Usage:
// <Avatar src="/images/user.jpg" alt="John Doe" size="lg" />
// ✅ Reusable avatar component
// ✅ Multiple sizes
// ✅ Rounded styling
// ✅ Type-safeExample 4: Image Gallery
import Image from 'next/image';
interface GalleryImage {
id: string;
url: string;
alt: string;
}
export function Gallery({ images }: { images: GalleryImage[] }) {
return (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{images.map(image => (
<div
key={image.id}
className="relative aspect-square overflow-hidden rounded-lg cursor-pointer group"
>
<Image
src={image.url}
alt={image.alt}
fill
className="object-cover group-hover:scale-110 transition-transform duration-300"
/>
{/* Hover overlay */}
<div className="absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">
<span className="text-white font-semibold">View</span>
</div>
</div>
))}
</div>
);
}
// ✅ Responsive masonry grid
// ✅ aspect-square for uniform sizing
// ✅ Hover effects
// ✅ Lazy loadingImage Files Structure
Organization of images in Next.js project
Select a file or folder to see details
Image Component Best Practices
1. Always Provide Width and Height
// ✅ GOOD: Dimensions prevent layout shift
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
/>
// ✅ GOOD: Or use fill with container
<div className="relative w-full h-96">
<Image src="/hero.jpg" alt="Hero" fill />
</div>
// ❌ BAD: Missing dimensions causes issues
<Image src="/hero.jpg" alt="Hero" />
// Error: Missing width/height or fill prop2. Use Priority for Above-the-Fold Images
// ✅ GOOD: priority for hero image
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority
/>
// ❌ BAD: No priority for above-fold
// Lazy loads hero image → poor LCP
// Use priority sparingly (1-2 images max)3. Provide Descriptive Alt Text
// ✅ GOOD: Descriptive alt text
<Image
src="/product.jpg"
alt="Blue wireless headphones with noise cancellation"
width={400}
height={400}
/>
// ❌ BAD: Generic or missing alt
<Image src="/product.jpg" alt="product" />
<Image src="/product.jpg" alt="" />
// Good alt text:
// - Describes what's in the image
// - Helps screen reader users
// - Improves SEO4. Use Appropriate Object Fit
// ✅ GOOD: object-cover for backgrounds
<div className="relative h-64">
<Image src="/bg.jpg" alt="Background" fill className="object-cover" />
</div>
// ✅ GOOD: object-contain for logos
<div className="relative h-32">
<Image src="/logo.png" alt="Logo" fill className="object-contain" />
</div>
// object-cover: Fills, crops
// object-contain: Fits, shows all5. Optimize for Different Devices
// ✅ GOOD: Responsive with fill
<div className="relative w-full h-64 md:h-96">
<Image src="/hero.jpg" alt="Hero" fill />
</div>
// ✅ GOOD: Responsive with className
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
className="w-full h-auto"
/>
// Next.js automatically serves appropriate sizeKey Takeaways
- Import from 'next/image' - use Image component, not <img>
- Automatic optimization - resizing, compression, format conversion
- Required props - src, alt, and width/height OR fill
- Lazy loading default - images load when entering viewport
- priority prop - disable lazy loading for above-fold images
- fill prop - make image fill parent container
- Remote images - configure domains in next.config.js
- No layout shift - dimensions prevent content jumping
What's Next?
You've mastered the Image component basics! Next, we'll explore Image Optimization and Best Practices—advanced techniques for sizing, formats, quality settings, responsive images, and performance optimization. You'll learn to squeeze every bit of performance from your images!
We'll cover image formats (WebP, AVIF), quality settings, responsive images with sizes prop, blur placeholders, and measuring image performance for Core Web Vitals.
⚡ Dramatic Performance Gains
The Image component typically reduces image sizes by 30-80% through modern formats and optimization. This dramatically improves page load times and Core Web Vitals. Always use Image instead of <img>!