Next.js Image Optimization — next/image Guide

Sanjeev SharmaSanjeev Sharma
4 min read

Advertisement

Next.js Image Optimization — next/image Guide

The next/image component automatically optimizes images for performance, handling responsive sizing, format conversion, and lazy loading.

Why next/image?

Standard HTML images have performance issues:

  • No automatic optimization for different screen sizes
  • No format conversion (WEBP, AVIF)
  • Layout shift while loading
  • Unnecessary data transfer to mobile devices

next/image solves all these problems.

Basic Usage

// app/page.tsx
import Image from 'next/image'
import heroImage from '@/public/hero.jpg'

export default function HomePage() {
  return (
    <Image
      src={heroImage}
      alt="Hero image"
      width={1200}
      height={600}
    />
  )
}

When using imported images, dimensions are automatic.

Remote Images

For remote URLs, specify dimensions:

import Image from 'next/image'

export default function BlogPost() {
  return (
    <Image
      src="https://cdn.example.com/posts/featured.jpg"
      alt="Featured image"
      width={800}
      height={400}
    />
  )
}

Responsive Images

Use sizes for responsive optimization:

import Image from 'next/image'

export default function ResponsiveImage() {
  return (
    <Image
      src="/hero.jpg"
      alt="Hero"
      width={1200}
      height={600}
      sizes="(max-width: 640px) 100vw,
             (max-width: 1024px) 75vw,
             50vw"
      priority
    />
  )
}

The sizes prop tells Next.js which image sizes to generate.

Lazy Loading

Images load lazily by default:

import Image from 'next/image'

export default function PostList() {
  return (
    <div>
      {posts.map(post => (
        <Image
          key={post.id}
          src={post.image}
          alt={post.title}
          width={300}
          height={200}
          loading="lazy" // Default
        />
      ))}
    </div>
  )
}

Use loading="eager" for above-the-fold images:

<Image
  src="/hero.jpg"
  alt="Hero"
  width={1200}
  height={600}
  priority // Same as loading="eager"
/>

Styling Images

Apply CSS to next/image:

import Image from 'next/image'

export default function StyledImage() {
  return (
    <Image
      src="/photo.jpg"
      alt="Photo"
      width={400}
      height={300}
      className="rounded-lg shadow-lg"
    />
  )
}

Or use a wrapper div:

import Image from 'next/image'

export default function WrappedImage() {
  return (
    <div className="relative w-full h-80">
      <Image
        src="/photo.jpg"
        alt="Photo"
        fill
        className="object-cover"
      />
    </div>
  )
}

The fill prop makes the image fill its container.

Fill Container Pattern

Use fill for unknown dimensions:

import Image from 'next/image'

export default function ResponsiveCard() {
  return (
    <div className="relative w-full aspect-video">
      <Image
        src="/card-image.jpg"
        alt="Card"
        fill
        className="object-cover rounded-lg"
      />
    </div>
  )
}

OnLoad and Error Handling

Handle image events:

'use client'

import { useState } from 'react'
import Image from 'next/image'

export default function ImageWithFallback() {
  const [hasError, setHasError] = useState(false)

  if (hasError) {
    return <div className="bg-gray-200 w-full h-64" />
  }

  return (
    <Image
      src="/image.jpg"
      alt="Image"
      width={600}
      height={400}
      onError={() => setHasError(true)}
      onLoadingComplete={(result) => {
        console.log('Image loaded:', result)
      }}
    />
  )
}

External Images Configuration

Allow external image domains:

// next.config.js
export default {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'cdn.example.com',
        port: '',
        pathname: '/assets/**'
      },
      {
        protocol: 'https',
        hostname: 'images.unsplash.com'
      }
    ]
  }
}

Image Placeholder

Show placeholder while loading:

import Image from 'next/image'

export default function ImageWithPlaceholder() {
  return (
    <Image
      src="/large-image.jpg"
      alt="Image"
      width={800}
      height={600}
      placeholder="blur"
      blurDataURL="data:image/svg+xml,%3Csvg..."
    />
  )
}

Optimized Formats

Next.js automatically serves optimal formats:

// Serves WebP on Chrome, AVIF on modern browsers, JPG fallback
<Image
  src="/image.jpg"
  alt="Optimized"
  width={800}
  height={600}
/>

Performance Tips

Preload Critical Images:

import { preloadImage } from 'next/image'

export default function Page() {
  preloadImage('/hero.jpg', 'image')

  return <Image src="/hero.jpg" alt="Hero" width={1200} height={600} />
}

Quality Control:

<Image
  src="/image.jpg"
  alt="Image"
  width={800}
  height={600}
  quality={85} // Default is 75
/>

Device Pixel Ratio:

<Image
  src="/image.jpg"
  alt="Image"
  width={800}
  height={600}
  priority
/>

Common Issues

Issue: Layout shift Solution: Always provide width/height or use fill with aspect ratio.

Issue: Blurry images on high-DPI screens Solution: Increase width/height or use quality prop.

FAQ

Q: Do I have to use next/image? A: No, but it's highly recommended for performance. Standard img tags work but miss optimization benefits.

Q: Can I use SVGs with next/image? A: SVGs work but don't get optimized. Use standard img tags for SVGs.

Q: How do I implement an image gallery? A: Map over images and render multiple next/image components with appropriate styling.


Master image optimization to significantly improve Core Web Vitals.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro