# next/image Deep Dive — Optimization, Lazy Loading, and the Props That Actually Matter

> Source: <https://dev.to/aon_infotech_3a1b6ff525fc/nextimage-deep-dive-optimization-lazy-loading-and-the-props-that-actually-matter-4kpg>
> Published: 2026-07-14 11:19:01+00:00

`next/image`

does a lot automatically — format conversion, lazy loading, preventing layout shift. But several props change behavior significantly, and getting them wrong either hurts performance or breaks things in production. Here's the complete picture of what actually matters, with the approach used in the image-heavy [generation results interface](https://pixova.io/blog/can-ai-generate-images-for-free).

When you use `<Image>`

from `next/image`

, you get automatically:

`sizes`

Prop — The Most Underused Optimization
The `sizes`

prop tells the browser what size the image will be at different viewport widths. Without it, Next.js can't serve optimally-sized images and defaults to generating several sizes that may be larger than needed.

```
// Without sizes — Next.js doesn't know the display size
<Image src="/hero.jpg" alt="Hero" fill />

// With sizes — Next.js serves the right size for each viewport
<Image
  src="/hero.jpg"
  alt="Hero"
  fill
  sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
```

Read the `sizes`

value as: "On mobile (under 768px), this image takes 100% of the viewport width. On tablets (under 1200px), 50%. On desktop, 33%."

Next.js uses this to calculate which image size to serve. Correct `sizes`

values can reduce image payload by 50-70% on mobile.

**Common patterns:**

```
// Full-width hero or banner
sizes="100vw"

// Half-width on desktop, full on mobile
sizes="(max-width: 768px) 100vw, 50vw"

// Card grid — 1 col mobile, 2 col tablet, 3 col desktop
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"

// Small thumbnail — always small
sizes="150px"
```

`priority`

— For Above-the-Fold Images
By default, `next/image`

lazy loads everything. For images visible immediately on page load (hero images, header images, above-the-fold content), lazy loading is counterproductive — you want these to load as fast as possible.

```
// Hero image — load immediately, don't lazy load
<Image
  src="/hero.jpg"
  alt="Page hero"
  width={1200}
  height={600}
  priority
/>
```

Add `priority`

to the first image visible on page load. This tells Next.js to preload it and not apply lazy loading.

**The rule:** One or two images maximum per page should have `priority`

. If you add it to many images, you've eliminated the performance benefit of lazy loading.

`fill`

vs Width/Height — When to Use Each
**Explicit width/height:** Use when you know the exact dimensions at render time. Generates precise sizes.

```
<Image
  src="/thumbnail.jpg"
  alt="Product thumbnail"
  width={300}
  height={200}
/>
```

** fill mode:** Use when the image should fill its container. The container must be

`position: relative`

(or `absolute`

or `fixed`

).

```
<div className="relative h-64 w-full">
  <Image
    src="/cover.jpg"
    alt="Article cover"
    fill
    sizes="100vw"
    className="object-cover"
  />
</div>
```

`className="object-cover"`

on the Image determines how it fills the container. `object-cover`

maintains aspect ratio and crops. `object-contain`

maintains aspect ratio with letterboxing.

`quality`

— When Default Isn't Right
Default quality is 75, which is a good balance. For specific cases you might adjust:

```
// Photographic images — default 75 is fine
<Image src="/photo.jpg" alt="Photo" width={800} height={600} />

// Icons or images with sharp lines — higher quality prevents artifacts
<Image src="/logo.png" alt="Logo" width={100} height={50} quality={90} />

// Background textures where quality difference isn't visible
<Image src="/texture.jpg" alt="" width={1200} height={800} quality={60} />
```

Higher quality = larger file size. The default is right for most cases.

`remotePatterns`

Config
By default, `next/image`

only optimizes images from your own domain. For external images, you need to configure allowed patterns:

``` js
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'cdn.example.com',
        port: '',
        pathname: '/images/**',
      },
      {
        protocol: 'https',
        hostname: '*.cdn-example.com', // wildcard subdomain
      },
    ],
  },
};

module.exports = nextConfig;
```

Be specific with `pathname`

patterns. Wildcard `/**`

allows any path on that host. More restrictive patterns are safer.

`placeholder`

Prop — Blur-Up Loading
For a smooth loading experience, `next/image`

supports a blur placeholder that shows while the full image loads:

```
// Static images — Next.js generates blurDataURL automatically at build time
import heroImage from '/public/hero.jpg';

<Image
  src={heroImage}
  alt="Hero"
  placeholder="blur"
  priority
/>
```

For dynamic images (external URLs), you need to provide the blur data yourself:

``` js
// Generate a tiny blurred placeholder (many services provide these)
const blurDataURL = 'data:image/jpeg;base64,/9j/4AAQ...'; // tiny base64 image

<Image
  src={dynamicImageUrl}
  alt="Dynamic image"
  width={800}
  height={600}
  placeholder="blur"
  blurDataURL={blurDataURL}
/>
```

The `placeholder="blur"`

approach significantly improves perceived loading performance for above-the-fold images.

To verify your image optimization is working:

`Img`

`Type`

column — should show `webp`

or `avif`

instead of `jpeg`

/`png`

`Size`

column vs original file size
A well-optimized image should be significantly smaller than the original and in a modern format.**Forgetting sizes on fill images.** Without

`sizes`

, Next.js can't optimize for viewport — generates full-size images for all viewports.**Adding priority to too many images.** It's for above-the-fold only. One or two per page maximum.

**Not configuring remotePatterns.** External images without pattern configuration fall back to unoptimized serving, breaking the performance benefits.

**Using <img> instead of <Image>.** Native

`<img>`

tags don't get any next/image optimization — no format conversion, no lazy loading, no layout stability. ESLint rule `next/no-img-element`

catches this.| Prop | When to use |
|---|---|
`sizes` |
Always, when using `fill` or when image width changes across viewports |
`priority` |
First 1-2 visible images on page load only |
`fill` |
When image should fill a container with unknown dimensions |
`quality` |
When default 75 is visibly too low (icons) or unnecessarily high |
`placeholder="blur"` |
Above-the-fold images where loading is visible to users |

The three that move the needle most: correct `sizes`

(biggest file size impact), `priority`

on LCP images (biggest perceived performance impact), and `remotePatterns`

configuration (required for external images to optimize at all).

If you serve images from a custom CDN with its own optimization parameters, you can configure a custom loader:

```
// next.config.js
module.exports = {
  images: {
    loader: 'custom',
    loaderFile: './imageLoader.js',
  },
};
// imageLoader.js
export default function myImageLoader({ src, width, quality }) {
  return `https://your-cdn.com/image?url=${src}&w=${width}&q=${quality || 75}`;
}
```

This gives you full control over how image URLs are constructed for optimization, useful when your CDN uses different query parameter formats than Next.js expects.

For most applications using standard hosting, the default loader is correct. Custom loaders are for specific CDN integrations that need URL format control.

If you're on an older Next.js version using `domains`

config, migrate to `remotePatterns`

:

```
// Old (deprecated)
images: {
  domains: ['cdn.example.com'],
}

// New (use this)
images: {
  remotePatterns: [
    { protocol: 'https', hostname: 'cdn.example.com' }
  ],
}
```

`remotePatterns`

is more secure — it supports pathname restrictions that `domains`

doesn't, preventing a compromised external hostname from serving arbitrary paths through your Next.js image optimizer.
