Skip to content

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 loading

Advantages 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

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

Content is for learning and research only.