/ โ† Image Optimisation Fixes / Images in Next.js
In this guide
  1. What next/image does and does not do for you
  2. The fix, step by step
  3. Configuration and component examples
  4. Hosting differences that affect images
  5. Edge cases
  6. Keeping a Next.js site image-healthy
  7. Questions and answers

What next/image does and does not do for you

When you render <Image> from next/image, Next.js outputs an ordinary img element whose src and srcset point to its optimisation endpoint, /_next/image, with the original address, a width and a quality in the query string. The first request for each combination is resized and re-encoded on the server, then cached. The component also adds loading="lazy" and decoding="async" by default and requires dimensions so space is reserved.

What it cannot know is how your layout uses the image. It does not read your CSS, so it cannot tell that a card is 300 pixels wide on desktop. That is the job of the sizes prop. It also cannot tell which image is the most important, so it lazy-loads all of them until you mark one as the priority.

Audit findingUsual Next.js causeFix
Oversized dimensionsNo sizes on a responsive or fill imageAdd a sizes prop matching the layout
Main image lazy-loadedDefault lazy loading left on the heroMark it for early loading
Old formatOnly WebP enabled, or images bypass the componentAdd AVIF to images.formats; use the component
Oversized fileRaw img tags or CSS backgrounds pointing at originalsMove to next/image or pre-size the files
Slow or uncached deliverySelf-hosted optimiser without persistent cache, or a short cache lifetimePersist the image cache or use a loader with a CDN

The general points about srcset, lazy loading and CDNs are covered in the sibling guides on image dimensions, lazy loading and CDN delivery. This page is about how those map onto Next.js.

The fix, step by step

Step 1

Find images that bypass the component

Search the project for raw <img tags, backgroundImage in styles, and images inserted from a CMS as HTML strings. None of these are resized or converted by Next.js. Run the Image Optimisation audit on a few routes and compare its list with what you find. Content images in your own components should move to next/image. Images inside CMS rich text can be mapped to the component with a custom renderer, or served already sized from the CMS image service.

Step 2

Allow remote sources with remotePatterns

Images from another host, such as a headless CMS or storage bucket, fail with an error until that host is permitted. In next.config, add an entry to images.remotePatterns with the protocol, hostname and, ideally, a pathname prefix such as /uploads/**. Keep patterns as narrow as you can, because anything allowed can be pulled through your optimiser at your expense. Replace the older images.domains setting if your project still uses it, as it is deprecated.

Step 3

Give every image its dimensions

Statically imported images, written as import hero from './hero.jpg', get their width, height and a blur placeholder automatically. Remote images need explicit width and height props giving the original's aspect ratio. When you do not know the size in advance, for instance user uploads, use the fill prop instead and place the image inside a parent with position: relative and a set height or aspect ratio, using a CSS class rather than inline styles.

Step 4

Add a sizes prop to responsive and fill images

Without sizes, an image with fixed width gets only 1x and 2x variants, and a fill image is assumed to be as wide as the viewport, so a small card can receive a file sized for a large desktop screen. Give every image that changes size with the layout a sizes prop that mirrors your CSS breakpoints, such as (min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw. This single prop fixes most oversized findings on Next.js sites.

Step 5

Load the main image early

Mark the one image that is usually the Largest Contentful Paint element, typically the hero or first product photo, so it is not lazy-loaded. In Next.js 16 the priority prop is deprecated in favour of preload, and for most cases the documentation suggests loading="eager" or fetchPriority="high" instead. On earlier versions use priority. Do it on one image per route, not every image, and pass the prop down from the page so shared components are not always eager.

Step 6

Turn on AVIF and tune widths and quality

By default the optimiser produces WebP only. Set images.formats to ['image/avif', 'image/webp'] so browsers that accept AVIF receive it first. AVIF takes longer to encode, so the first request for each variant is slower, but later ones are cached. Review deviceSizes so the widths match your real breakpoints, and list the quality values you use in images.qualities, which recent versions require for any quality other than the default.

Step 7

Choose where optimisation runs

On Vercel, the platform runs the optimiser and its usage counts against your plan, so check the usage page after enabling AVIF or adding widths. When self-hosting with next start, the server does the work and caches results under .next/cache/images, which must survive deployments. With output: 'export' there is no server, so set images.unoptimized to true and pre-size images, or configure a custom loader that builds URLs for an image CDN.

Step 8

Check the rendered HTML and cache headers

View the page source rather than the React tree, and confirm each important image has a sensible srcset, a sizes attribute, and that the hero has no loading="lazy". In the Network panel, check that image responses come back as AVIF or WebP, at widths near the slot size, and with a long cache-control on repeat requests. Test at phone and desktop widths, then run the Image Optimisation audit again on the same routes.

Configuration and component examples

next.config.js

/** @type {import('next').NextConfig} */
module.exports = {
  images: {
    formats: ['image/avif', 'image/webp'],
    remotePatterns: [
      { protocol: 'https', hostname: 'cms.example.com', pathname: '/uploads/**' },
    ],
    deviceSizes: [640, 828, 1080, 1280, 1920],
    qualities: [60, 75],
  },
};

Keep the lists short. Every extra width and quality multiplies the number of variants the optimiser may have to produce and cache for each image.

A hero image on a route

import Image from 'next/image';
import hero from '@/public/img/fellside-hero.jpg';

export default function Home() {
  return (
    <Image
      src={hero}
      alt="Walkers on a ridge above Buttermere in autumn"
      sizes="100vw"
      className="hero-img"
      fetchPriority="high"
      loading="eager"
      placeholder="blur"
    />
  );
}

The static import supplies width, height and a blur placeholder. On versions before 16, replace the last two loading props with priority.

A product card using fill

// ProductCard.jsx
<div className="card-media">
  <Image
    src={product.imageUrl}
    alt={product.imageAlt}
    fill
    sizes="(min-width: 1024px) 25vw, (min-width: 640px) 50vw, 100vw"
    className="card-img"
  />
</div>

/* styles.css */
.card-media { position: relative; aspect-ratio: 4 / 5; }
.card-img { object-fit: cover; }

A custom loader for an image CDN

// image-loader.js
export default function loader({ src, width, quality }) {
  const q = quality || 75;
  return `https://img.example.com${src}?w=${width}&q=${q}&auto=format`;
}

// next.config.js
module.exports = {
  images: { loader: 'custom', loaderFile: './image-loader.js' },
};

With a custom loader, Next.js still builds the srcset from your configured widths, but each URL points at the CDN, which does the resizing. This is the usual answer for static export and for sites that already pay for an image service.

Hosting differences that affect images

Vercel

Image optimisation is built in and cached at the edge. It is metered, so a site with many remote images, many widths and AVIF enabled uses more than one with a few static images. Narrow remotePatterns and short width lists keep usage predictable. Check the current plan limits in your dashboard rather than assuming.

Self-hosted Node server or container

Recent versions of Next.js use the Sharp library automatically for next start and standalone output. The optimised files are written to .next/cache/images. In containers or on platforms that rebuild the file system on every deployment, that cache is lost, so each variant is regenerated on the first visit after each release. Mount a persistent volume for the cache, or put a CDN in front that respects the long cache headers the optimiser sends.

The images.minimumCacheTTL setting controls how long optimised images are cached when the original has no longer cache header of its own. Next.js 16 raised its default considerably; on older versions it was short enough to cause frequent re-optimisation, so set it explicitly if you are on an older release.

Static export

With output: 'export' the site is plain files with no server to run the optimiser. The build fails for images using the default loader unless you set images.unoptimized to true or configure a custom loader. With unoptimized, the component still reserves space and lazy-loads, but serves your files exactly as they are, so they must already be sized and converted.

โš ๏ธ Setting unoptimized to fix a build error, and then forgetting about it, is a common reason a Next.js site suddenly fails image findings. If you see original-sized JPEGs in the Network panel, check for this setting first.

Edge cases

Keeping a Next.js site image-healthy

Questions and answers

1. Should I use next/image for every image in a Next.js site?

For content images such as heroes, product photos and article images, yes, because it handles resizing, modern formats, lazy loading and reserved space in one place. There are sensible exceptions: SVG logos and icons, which do not need optimisation; images already served at the right size by a CMS or CDN, which can use a custom loader; and purely decorative backgrounds, which belong in CSS. Keep the ESLint warning on so exceptions are deliberate.

2. Why does next/image send such large images to phones?

Almost always because the sizes prop is missing. Without it, an image with a fixed width only gets one and two times density variants, and a fill image is assumed to span the full viewport. The browser then picks a file sized for that assumption. Add a sizes prop that describes how wide the image is at each breakpoint, and check the chosen file in the Network panel at a phone width afterwards.

3. What happened to the priority prop in Next.js 16?

Next.js 16 deprecated the priority prop on the Image component and introduced preload to take its place. The documentation suggests that in most cases you should instead set loading to eager or fetchPriority to high on the main image, which avoids adding a separate preload tag. On Next.js 15 and earlier, priority is still the way to mark the hero. Whichever you use, apply it to one image per route.

4. Why am I getting an error that the hostname is not configured?

Next.js refuses to optimise images from hosts you have not allowed, to stop anyone using your server to resize arbitrary images. Add the host to images.remotePatterns in next.config with its protocol, hostname and ideally a pathname prefix, then restart the development server. Avoid wildcards that allow any host. If the images come from a service that already resizes them, a custom loader may be a better choice than allowing the host.

5. How do I use next/image with static export?

Static export produces plain files with no server to run the optimiser, so the default loader cannot work. You have two options. Set images.unoptimized to true and make sure every image is already resized and converted before the build, or configure a custom loader that points image URLs at an image CDN which resizes on request. The component still reserves space, applies lazy loading and builds srcset with either approach.

6. Why are images slow the first time after each deployment?

The optimiser creates each width and format the first time it is requested and caches the result. On self-hosted setups the cache lives in the .next/cache/images folder, and if that folder is wiped on each deployment, every variant must be generated again. Enabling AVIF makes those first requests slower still. Keep the cache on a persistent volume or put a CDN in front, and consider warming key pages after release.

7. Does enabling AVIF in Next.js have any downsides?

AVIF files are usually smaller than WebP at similar quality, but encoding them takes more processing time. The first request for each variant is therefore slower, and on metered platforms the extra work may count against your usage. Once cached, delivery is fast. For most sites the smaller files are worth it; if first-request speed or usage limits matter a lot, keep WebP only or pre-generate AVIF files at build time.

Audit the images your Next.js pages actually send

The Image Optimisation audit checks every image on your page for file size, format, dimensions, alt text, lazy-loading, layout shift and CDN delivery, and ranks its recommendations by their effect on loading and bandwidth.

Open Image Optimisation Fixes โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All Image Optimisation Fixes  ·  Core Web Vitals in Next.js  ·  Fixing Wrong Image Dimensions  ·  Fixing Image CDN Delivery  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.