Optimizing Images in Next.js 15

TarunTarun
5 min read

Introduction to next/image

Next.js provides an optimized <Image> component that enhances performance, responsiveness, and loading efficiency. Unlike the standard <img> tag, next/image automatically optimizes images, reducing load times and improving performance.

Why Use next/image?

Next.js provides an optimized <Image> component that enhances performance, responsiveness, and loading efficiency. Reasons to use it:

  • Automatic Optimization: Images are automatically resized and served in modern formats (e.g., WebP, AVIF).

  • Lazy Loading: By default, images are loaded only when they appear in the viewport.

  • Responsive Images: Automatically adjusts size based on device resolution.

  • Built-in Caching & CDN Support: Works well with Vercel’s Image Optimization API and custom loaders.

  • Better SEO & Performance: Reduces Largest Contentful Paint (LCP) and improves Core Web Vitals.


Setting Up Images in Next.js

1. Storing Local Images

  • Place local images inside the /public folder.

  • Example file structure:

      /public
        ├── example.jpg
        ├── images/
            ├── logo.png
    

2. Using Local vs. External Images

ScenarioBest Practice
Static assets like logos, iconsStore in /public and import
CMS or API-fetched imagesUse external URLs with next.config.js setup
Dynamic user-uploaded imagesStore in cloud services (e.g., Cloudinary, S3)

How to Use <Image> Effectively

To use the <Image> component, import it from next/image:

import Image from "next/image";

export default function Example() {
  return (
    <Image
      src="/example.jpg"
      alt="Example Image"
      width={500}
      height={300}
    />
  );
}

Key Points:

  • src: Path to the image (local or external).

  • alt: Improves accessibility and SEO.

  • width & height: Required for static images.

  • Automatic lazy loading (enabled by default).

  • Automatic format conversion (e.g., JPEG to WebP for better compression).


When and How to Use Local vs. External Images

1. Local Images (Static Imports)

When to Use:

  • Best for images that do not change frequently.

  • Benefits from Next.js’s built-in optimization and caching.

How to Use:

import exampleImage from "@/public/example.jpg";

<Image src={exampleImage} alt="Static Import" width={500} height={300} />
  • Use width and height to define static dimensions.

  • No need for a next.config.js update.

import exampleImage from "@/public/example.jpg";

<Image src={exampleImage} alt="Static Import" />
  • Next.js automatically detects the dimensions of static imports.

  • Not recommended if you need explicit control over image size.

2. External Images (From CMS or Remote URLs)

When to Use:

  • When fetching images from CMS, APIs, or cloud storage (e.g., Cloudinary, Pexels, AWS S3).

How to Use:

  • Update next.config.js to allow external domains:
module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "example.com",
      },
    ],
  },
};
  • Use <Image> with a remote URL:
<Image src="https://example.com/image.jpg" alt="External" width={500} height={300} />
  • For dynamic images, use sizes instead of fixed width and height.

  • Use fill for fluid layouts when the parent container determines size.


Achieving Responsiveness with <Image>

1. Using fill for Flexible Layouts

import Image from "next/image";

export default function ResponsiveImage() {
  return (
    <div style={{ position: "relative", width: "100%", height: "300px" }}>
      <Image src="/example.jpg" alt="Responsive" fill style={{ objectFit: "cover" }} />
    </div>
  );
}

🔹 fill allows images to stretch within their parent container.

2. Using sizes for Different Screen Widths

<Image
  src="/example.jpg"
  alt="Adaptive Image"
  width={800}
  height={500}
  sizes="(max-width: 768px) 100vw, 50vw"
/>

🔹 sizes tells Next.js how large the image should be at different viewport widths.

3. When You Don't Know the Image Dimensions

If you know the dimensions of the container but not the image:

<div style={{ width: "100%", height: "300px", position: "relative" }}>
  <Image src="/example.jpg" alt="Unknown Size" fill style={{ objectFit: "contain" }} />
</div>

🔹 Ensures the image fits properly without needing exact dimensions.


Optimizing Images for Performance

1. Prioritizing Important Images

Use the priority prop for images that need to load ASAP (e.g., hero banners):

<Image src="/banner.jpg" alt="Banner" width={1200} height={600} priority />

🔹 Skips lazy loading and preloads the image.

2. Using quality to Reduce File Size

<Image src="/image.jpg" alt="Optimized" width={500} height={300} quality={75} />

🔹 Lowers file size while maintaining decent quality (default: 75).

3. Implementing Blur Placeholder for Better UX

<Image 
    src="/image.jpg" alt="Blurred" 
    width={500} height={300} 
    placeholder="blur" 
    blurDataURL="data:image/png;base64,..." 
/>

🔹 Shows a low-quality blurred version while loading.

For external images (like the ones where src is an external link as you mentioned), the placeholder="blur" property requires a corresponding blurDataURL property since Next.js can't automatically generate blur placeholders for remote images.

The blurDataURL should be:

  1. A base64-encoded image URL (usually very small, low-quality version of your image)

  2. Typically under 10KB in size

  3. In the format data:image/jpeg;base64,ENCODED_DATA

You have a few options for generating this:

  1. Use a library like plaiceholder or blurhash to generate it

  2. Use a simple color placeholder (simple base64 encoded small colored image)

  3. Create a standard placeholder that you use for all your images

4. Disabling Lazy Loading for Above-the-Fold Content

<Image src="/logo.png" alt="Logo" width={200} height={100} loading="eager" />

🔹 Use loading="eager" to load crucial images immediately.


Understanding Lazy Loading and Viewport-Based Loading

How Lazy Loading Works

  • By default, Next.js images are lazy-loaded.

  • Images below the fold are only loaded when they enter the viewport.

  • Better for performance since fewer initial resources are downloaded.

Observing Image Loading Behavior

  • When resizing the viewport, different image versions are served.

  • You can check lazy loading using Chrome DevTools → Network tab.


Conclusion

Using next/image in Next.js 15 significantly improves performance, enhances responsiveness, and provides automatic optimizations. By implementing best practices like prioritization, quality adjustments, and correct sizing, you can ensure fast-loading, scalable, and visually appealing applications. 🚀

0
Subscribe to my newsletter

Read articles from Tarun directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Tarun
Tarun

I have 3 year-experience as software developer skilled in ReactJS, TypeScript, NextJS, Redux.