React

Optimising images with next/image

Dimensions, sizes, priority and formats: what next/image really does, what you still have to declare, and where optimisation stops.

Key points

  • `width` and `height` reserve space before the download: the main defence against layout shift.
  • `sizes` must describe the image's real width, not the window width.
  • One `priority` image per page, the one that drives LCP.

next/image solves three problems: declaring dimensions before the download, serving the right format and width, and loading everything late except the main image. Skip one and the gain never shows up in Core Web Vitals.

Dimensions, always

import Image from "next/image";

export function Hero() {
  return (
    
  );
}

With width and height the browser reserves the space before the file arrives: that is the difference between a layout that jumps and one that holds. The ratio comes from those two numbers, not from the downloaded file, so they must match the real proportions.

sizes decides which file you download

Without sizes, the image is treated as full width and the browser downloads a larger file than needed. Declare the width the image really occupies in the layout: a card in a three-column grid is one third, not 100 per cent.


  

With fill the image takes over the container: the container must be positioned and must have a height, otherwise the height is zero and nothing renders. An explicit aspect-ratio avoids depending on the load.

The LCP image does not load late

The largest image above the fold is the one that counts for LCP and it must not go through lazy loading. priority preloads it and raises the request priority; in newer releases the prop is called preload. One image per page: putting it everywhere cancels the effect and takes bandwidth from the rest.

What the optimizer answers

In development and in production the image is not served from its original path but from an endpoint that resizes and reconverts it. The response states the chosen format and the cache applied: it is the fastest way to check whether optimisation is running at all.

curl -sI "http://localhost:3000/_next/image?url=%2Fhero.jpg&w=640&q=75"

Formats and external domains

AVIF weighs less than WebP, and WebP less than JPEG; the browser picks from what you offer. Images on an external domain need remotePatterns: without it the request is rejected and the image never appears. An SVG needs no conversion: unoptimized avoids a pointless pass.

// next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  images: {
    formats: ["image/avif", "image/webp"],
    remotePatterns: [
      { protocol: "https", hostname: "images.example.com" },
    ],
  },
};

export default nextConfig;

The cost, measured

The article covers on this site are generated PNGs served as they are, because social scrapers read them too. The average says a cover stays under 100 KB: fine for a preview, worth converting for an image inside a page.

Where catalogue components fit

Galleries need stable proportions before the images arrive: the Gallery 4 record shows a composed grid, Image Comparison Slider a case with two overlapping images, and the hero blocks the place where the LCP image lives. The code lands in your repository: you declare the dimensions.

Mistakes you see in production

  • Setting the wrong width and height to avoid touching the CSS: the ratio stays wrong and so does CLS.
  • Using fill inside an unpositioned container: the image collapses to zero pixels.
  • priority on every image on the page: none is higher priority and the bandwidth is split.
  • A global unoptimized to silence a remotePatterns error: you lose the whole advantage.

One limitation to name

With output: "export" there is no optimizer: next/image shrinks to an img tag and you serve the alternative formats yourself. The same holds behind a CDN that blocks the /_next/image endpoint: configure the loader in that case, do not switch optimisation off.

The link between images and layout shift is explained in layout shift and images without dimensions; the thresholds to meet are in what Core Web Vitals are. If you are choosing a framework, Astro vs Next.js compares two opposite ways of handling images.

Frequently asked questions

Does next/image work for SVG icons?

No. An SVG needs no resizing: use an img tag, or pass unoptimized to skip a pointless conversion.

Why is my image invisible with fill?

Because the container is not positioned or has no height. Add position relative and an aspect ratio to the container.

How many images can I mark priority?

One, the largest one above the fold. With two or more the priority cancels out and the browser downloads everything together.

AVIF or WebP?

AVIF weighs less, WebP decodes faster on old hardware. Offer both and let the browser choose.

Can next/image load an external URL?

Yes, after adding the domain to remotePatterns in the image configuration. Without that entry the request is rejected.