WebP Images in Next.js: Auto-Optimise With next/image
Next.js automatically serves WebP via the next/image component. Learn how to configure formats, quality, loaders, and priority for optimal performance.
Next.js includes built-in image optimisation through the next/image component. By default it automatically converts and serves images as WebP (or AVIF) based on the browser’s Accept header, with no additional configuration required. You write your imports and <Image> tags pointing to JPEG or PNG source files, and Next.js handles format conversion, resizing, and caching transparently.
Basic Usage #
Import Image from next/image and provide src, alt, width, and height. Next.js does the rest.
import Image from 'next/image';
export default function Hero() {
return (
<Image
src="/images/hero.jpg"
alt="Hero image"
width={1200}
height={600}
priority
/>
);
}
The width and height props set the rendered size and aspect ratio. Next.js generates multiple responsive variants at build time (or on first request in production) and injects the correct srcset automatically.
Add the priority prop to any <Image> that appears above the fold. It disables lazy loading and injects a <link rel="preload"> tag in <head>, which directly improves your Largest Contentful Paint (LCP) score.
How Next.js Handles WebP #
The Image Optimisation API lives at /_next/image and acts as an on-demand image transform pipeline:
- Request interception — The browser requests an image through
/_next/image?url=...&w=...&q=.... Next.js reads the browser’sAcceptheader to determine which format to serve. - Format conversion — Next.js converts the source image to WebP (or AVIF if configured and supported) using the
sharplibrary under the hood. - Caching — The converted image is cached on disk. Subsequent requests for the same URL, width, and quality are served directly from cache without re-processing.
- Vary header — Responses include
Vary: Acceptso CDN edge nodes cache WebP and non-WebP responses separately for the same URL.
Configuring Image Formats #
Customise the format priority, quality, and device breakpoints in next.config.js.
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
formats: ['image/avif', 'image/webp'], // AVIF first, WebP fallback
quality: 80,
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
},
};
module.exports = nextConfig;
formats controls which optimised formats are attempted in order. Browsers that support AVIF receive AVIF; browsers that support only WebP receive WebP; all others receive the original format. Remove 'image/avif' from the array if you want to serve WebP only — AVIF encoding is significantly slower and may increase cold-start times on large images.
Responsive Images With fill #
Use the fill prop when the image should expand to fill its parent container. Pair it with a positioned wrapper element and object-fit to control cropping behaviour.
<div style={{ position: 'relative', width: '100%', height: '400px' }}>
<Image
src="/images/photo.jpg"
alt="Photo"
fill
style={{ objectFit: 'cover' }}
/>
</div>
When using fill, omit the width and height props — the component reads its dimensions from the CSS layout instead. The parent element must have position: relative, position: absolute, or position: fixed.
External Images #
To optimise images hosted on external domains, add the allowed hostnames to remotePatterns in next.config.js. Next.js rejects external URLs that aren’t explicitly listed as a security measure.
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'images.example.com',
port: '',
pathname: '/uploads/**',
},
{
protocol: 'https',
hostname: '**.cdn-provider.com',
},
],
},
};
module.exports = nextConfig;
Once listed, external images are fetched, converted to WebP, cached, and served through /_next/image identically to local assets.
Custom Loaders #
If you use a third-party image CDN such as Cloudinary, Imgix, or Akamai, you can replace the default optimisation API with a custom loader that builds the CDN URL directly.
// lib/imageLoader.js
export default function cloudinaryLoader({ src, width, quality }) {
const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality || 'auto'}`];
return `https://res.cloudinary.com/demo/image/upload/${params.join(',')}${src}`;
}
import Image from 'next/image';
import cloudinaryLoader from '../lib/imageLoader';
<Image
loader={cloudinaryLoader}
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
/>
Custom loaders bypass /_next/image entirely, so the CDN handles format negotiation and conversion instead of Next.js.
Next.js Image Optimisation requires a Node.js server at runtime. If you export your site as fully static using
next export(oroutput: 'export'in Next.js 13+), the/_next/imageendpoint is unavailable. For static exports, use a custom loader pointing to an external image CDN, or pre-convert your images to WebP at build time before deploying.
Per-Image Quality Override #
Override the global quality setting on individual <Image> components using the quality prop.
<Image
src="/images/thumbnail.jpg"
alt="Thumbnail"
width={200}
height={200}
quality={65} // lower quality for small thumbnails
/>
<Image
src="/images/portfolio-hero.jpg"
alt="Portfolio hero"
width={1600}
height={900}
quality={90} // higher quality for showcase images
priority
/>
