Skip to content
jsmanifest logojsmanifest

Next.js Image Optimization in 2026: AVIF Config, remotePatterns, and the Settings Teams Miss

Next.js Image Optimization in 2026: AVIF Config, remotePatterns, and the Settings Teams Miss

The next/image config changes that break production image pipelines: the AVIF opt-in, the remotePatterns migration, the qualities allowlist, and the cache settings teams miss.

Correction (2026-11-02): There is no "next/image v3" or "v4" - Next.js does not version the Image component that way, so every comparison below is phrased against the actual release that introduced or changed a setting instead. next/image defaults to WebP, not AVIF - AVIF must be explicitly added to the formats array. The unoptimized prop still bypasses optimization without any loader configuration, the default minimumCacheTTL is 14400 seconds (4 hours), and the default contentDispositionType is attachment, not inline. Sections below are corrected accordingly.

Next.js Image Optimization in 2026: AVIF Config, remotePatterns, and the Settings Teams Miss

Most Next.js image performance problems in 2026 stem from teams enabling AVIF without understanding the configuration and breaking changes that silently degrade production pipelines. The next/image component defaults to WebP output, and teams who opt into AVIF by adding it to the formats array get a format that delivers 20-30% smaller file sizes at equivalent quality, but introduces browser compatibility gaps and configuration requirements that break existing deployments if fallback formats aren't configured too.

The failure mode here is subtle but expensive. Applications enable AVIF without configuring a WebP fallback, encoding begins server-side, and teams observe slower image response times on older browsers that have no acceptable format to fall back to. The configuration changes that come with it, explicit formats arrays, updated remotePatterns replacing deprecated domains, and new cache control settings, are not surfaced clearly during adoption. Production incidents follow when CDN integration breaks, disk cache limits are exceeded, or images fail to load from third-party sources that require the new security model.

%% alt: Image optimization problem flow showing silent AVIF encoding
flowchart LR
    A("Upgrade to Next.js 15") --> B("AVIF encoding starts by default")
    B --> C("Older browsers request fallback")
    C --> D("Server regenerates WebP on demand")
    D --> E("Response times spike")
    style E stroke:#fbbf24,fill:#3a2f0b,color:#fef3c7
Image optimization problem flow showing silent AVIF encoding

This matters because image optimization accounts for 40-60% of total page weight in modern web applications. When the optimization layer silently shifts format priorities without corresponding infrastructure updates, the performance wins teams expect from upgrading evaporate. The solution requires explicit configuration that maintains format flexibility while enabling AVIF where supported, paired with cache strategies that prevent redundant encoding work.

%% alt: Correct image optimization with explicit format control
flowchart LR
    A("Upgrade to Next.js 15") --> B("Set explicit formats array")
    B --> C("Configure remotePatterns")
    C --> D("Define cache limits")
    D --> E("Browsers receive optimal format")
    style E stroke:#34d399,fill:#0b3b2e,color:#d1fae5
Correct image optimization with explicit format control

Key Takeaways

  • WebP is the default format in next/image (formats: ['image/webp']); AVIF is never enabled automatically and requires explicit formats configuration, with array order controlling which format browsers get first.
  • The domains configuration is deprecated in favor of remotePatterns, which enforces stricter security through protocol, hostname, and pathname matching—breaking existing third-party image integrations.
  • Cache control settings (maximumDiskCacheSize, contentDispositionType) prevent disk exhaustion and enable CDN caching, but teams miss these during upgrades, leading to storage failures and cache bypass.
  • AVIF delivers 20-30% smaller file sizes than WebP at equivalent visual quality, but encoding time increases 3-5x and older browsers require fallback paths that must be explicitly configured.
  • The priority prop and sizes attribute are commonly misconfigured—priority images require manual preload link injection in layouts, and incorrect sizes generate oversized variants that negate optimization gains.

Enabling AVIF in next/image: Configuration and Breaking Changes

Next.js ships next/image with formats: ['image/webp'] as the default. AVIF is not in the default array, teams who want it must add 'image/avif' themselves.

Broader browser support makes AVIF a more viable opt-in choice than it used to be: AVIF support crossed 90% global coverage in late 2025. The format delivers superior compression ratios compared to WebP, particularly for photographic content with gradients and high-frequency detail. A typical product image that compresses to 80KB as WebP encodes to 55-60KB as AVIF at perceptually identical quality.

%% alt: Format priority flow in next/image
flowchart TD
    A("Browser requests image") --> B{"AVIF support?"}
    B -->|Yes| C("Serve AVIF variant")
    B -->|No| D{"WebP support?"}
    D -->|Yes| E("Serve WebP variant")
    D -->|No| F("Serve original format")
    style C stroke:#34d399,fill:#0b3b2e,color:#d1fae5
    style E stroke:#7c9cf0,fill:#142544,color:#eaf2ff
Format priority flow in next/image

The breaking change surfaces when teams add AVIF to formats without also keeping a WebP fallback in the array. Applications serving images to users on Safari 15 or older Android browsers without explicit fallback configuration will trigger AVIF encoding attempts that fail silently. The image component detects lack of support via the Accept header and regenerates WebP variants on demand, but this introduces latency spikes on first request and doubles optimization work server-side.

The implication here is that teams must audit their user base browser distribution before enabling AVIF by default. If analytics show 5%+ traffic from pre-AVIF browsers, the cost of redundant encoding outweighs the compression benefit. The correct approach is explicit format configuration in next.config.js:

import type { NextConfig } from 'next'
 
const config: NextConfig = {
  images: {
    formats: ['image/webp', 'image/avif'], // WebP first, AVIF fallback
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
    minimumCacheTTL: 60,
  },
}
 
export default config

This configuration prioritizes WebP, serves AVIF only to browsers that explicitly request it via Accept: image/avif, and maintains backward compatibility with the single-format default. Teams can invert the array to ['image/avif', 'image/webp'] once their analytics confirm AVIF support exceeds 95%.

The unoptimized prop still works exactly as it always has: setting unoptimized={true} (or the equivalent images.unoptimized: true in next.config.js) bypasses the optimization pipeline entirely and serves the original image directly, with no loader configuration required. Applications relying on unoptimized for SVG files or assets served from external CDNs don't need to migrate anything on this front, the prop remains the correct, supported way to skip processing.

Configuration Changes Teams Miss: formats, qualities, and remotePatterns

The domains array in next.config.js has been deprecated since Next.js 14, replaced by remotePatterns (stable since Next.js 12.3) which enforces protocol and pathname matching for third-party image sources.

%% alt: Remote pattern validation flow
flowchart LR
    A("Image source URL") --> B("Match protocol")
    B --> C("Match hostname")
    C --> D("Match pathname pattern")
    D --> E("Allow optimization")
    E --> F("Serve optimized image")
    style F stroke:#34d399,fill:#0b3b2e,color:#d1fae5
Remote pattern validation flow

The old domains configuration accepted hostnames only:

// Deprecated since Next.js 14
const config: NextConfig = {
  images: {
    domains: ['cdn.example.com', 'assets.partner.com'],
  },
}

This approach allowed any path on the specified domain, creating a security surface where attackers could reference arbitrary URLs under approved domains. The remotePatterns array requires explicit protocol, hostname, and optional pathname and port matching:

import type { NextConfig } from 'next'
 
const config: NextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'cdn.example.com',
        pathname: '/images/**',
      },
      {
        protocol: 'https',
        hostname: 'assets.partner.com',
        port: '',
        pathname: '/product-photos/**',
      },
    ],
  },
}
 
export default config

The ** glob pattern matches any nested path structure. Applications serving images from user-generated content platforms or third-party e-commerce APIs must explicitly enumerate each allowed pathname pattern. The failure mode teams encounter is production incidents where images load during local development (because remotePatterns validation only runs in production builds) but break after deployment when the Next.js optimizer rejects URLs that don't match the configured patterns.

The related configuration change that teams miss is the qualities allowlist, added in Next.js 14.2.23 and required starting in Next.js 16 (default [75]). It does not give AVIF and WebP separate quality settings; quality is still a single value set per <Image> instance. What qualities does is restrict which integers the optimizer will accept at all: a quality prop outside the list gets coerced to the closest allowed entry, and a direct request to the optimizer API with a disallowed quality gets a 400:

const config: NextConfig = {
  images: {
    formats: ['image/avif', 'image/webp'],
    deviceSizes: [640, 750, 828, 1080, 1200, 1920],
    qualities: [50, 65, 75, 90],
    dangerouslyAllowSVG: false,
    contentDispositionType: 'inline',
  },
}

Teams still get AVIF-appropriate quality by passing a lower explicit quality prop on the <Image> component for AVIF-heavy pages, since AVIF reaches visually comparable results at lower quality values than WebP. A WebP image at quality 80 is often visually close to AVIF at quality 65-70. The cost of skipping this is oversized AVIF files that negate the format's compression advantage. Teams should benchmark quality settings with real content using tools like ImageMagick's compare or browser DevTools, then add whichever values they land on to the qualities allowlist so the optimizer actually serves them.

The configuration surface also includes contentSecurityPolicy for SVG files (when dangerouslyAllowSVG: true) and contentDispositionType, which controls whether browsers download or display images inline. The default is attachment (forces a download), so applications that want images to render inline need to explicitly set contentDispositionType: 'inline'.

AVIF vs WebP in Production: Real Performance Impact

AVIF delivers 20-30% smaller file sizes than WebP at equivalent visual quality, but Next.js's own docs put the encoding cost at roughly 50% longer, creating latency tradeoffs that depend on cache hit rates.

%% alt: Format comparison showing AVIF benefits and encoding cost
flowchart LR
    A("Source image") --> B["WebP encoding 200ms"]
    A --> C["AVIF encoding 300ms"]
    B --> D("WebP 80KB")
    C --> E("AVIF 55KB")
    D --> F("First request 200ms")
    E --> G("First request 300ms")
    style D stroke:#7c9cf0,fill:#142544,color:#eaf2ff
    style E stroke:#34d399,fill:#0b3b2e,color:#d1fae5
    style G stroke:#fbbf24,fill:#3a2f0b,color:#fef3c7
Format comparison showing AVIF benefits and encoding cost

The performance impact manifests in two phases: cold-start encoding latency and ongoing bandwidth savings. When a user requests an image variant that hasn't been cached, the Next.js optimizer encodes it on-demand. WebP encoding for a 1920px product photo completes in 150-250ms on a modern server instance. The same image as AVIF requires roughly 225-375ms, about 50% longer, due to the format's more computationally intensive encoding algorithm.

This distinction is critical for applications with high image variety and low cache hit rates. E-commerce platforms serving thousands of unique SKU images or content management systems with frequent uploads will observe higher p99 latency on initial image loads when AVIF is enabled. The bandwidth savings compound over time—a site serving 10M image impressions monthly saves 2-3TB of transfer when AVIF replaces WebP—but the encoding cost concentrates at cache misses.

The mitigation strategy is aggressive caching with high TTLs and pre-warming for critical images:

import type { NextConfig } from 'next'
 
const config: NextConfig = {
  images: {
    formats: ['image/avif', 'image/webp'],
    minimumCacheTTL: 31536000, // 1 year for immutable images
    deviceSizes: [640, 750, 828, 1080, 1200, 1920],
  },
}
 
export default config

Applications using CDNs like Cloudflare or Fastly should configure aggressive cache rules that store optimized variants at the edge. The Next.js optimizer sets Cache-Control headers based on minimumCacheTTL, but CDN behavior depends on additional configuration. Vercel deployments automatically cache optimized images at the edge, but self-hosted Next.js applications must configure CDN cache keys that include the image URL and requested dimensions.

The browser support gap for AVIF narrows monthly but remains relevant for applications targeting older devices. Safari added AVIF support in version 16 (September 2022), but iOS users on older hardware remain on Safari 15. Teams can implement progressive enhancement by serving WebP to these users via explicit format ordering:

const config: NextConfig = {
  images: {
    // Serve WebP first, AVIF to browsers that request it
    formats: ['image/webp', 'image/avif'],
  },
}

The browser sends an Accept header listing supported formats. When Accept: image/avif,image/webp,*/* appears, Next.js serves AVIF. Older browsers send Accept: image/webp,*/* and receive WebP. This approach eliminates encoding waste—AVIF variants are only generated when browsers explicitly request them.

Custom Loaders and CDN Integration: When to Move Beyond Built-in Optimization

Applications serving 1M+ monthly image impressions or requiring advanced transformations should migrate to custom loaders that offload optimization to dedicated CDN services.

The built-in Next.js optimizer runs on the application server, consuming CPU and memory during encoding. High-traffic sites experience resource contention when image optimization competes with application request processing. The solution is a custom loader that delegates to Cloudinary, Imgix, or Cloudflare Images:

// lib/cloudinary-loader.ts
import type { ImageLoader } from 'next/image'
 
const cloudinaryLoader: ImageLoader = ({ src, width, quality }) => {
  const params = [
    'f_auto', // Auto format (AVIF/WebP based on browser)
    'c_limit', // Don't upscale
    `w_${width}`,
    `q_${quality || 'auto'}`,
  ]
  
  const baseUrl = 'https://res.cloudinary.com/your-cloud/image/upload'
  return `${baseUrl}/${params.join(',')}/${src}`
}
 
export default cloudinaryLoader
// next.config.ts
import type { NextConfig } from 'next'
 
const config: NextConfig = {
  images: {
    loader: 'custom',
    loaderFile: './lib/cloudinary-loader.ts',
  },
}
 
export default config

This configuration bypasses the Next.js optimizer entirely. The Image component generates Cloudinary URLs with transformation parameters, and Cloudinary handles format negotiation, encoding, and edge caching. The advantage is zero server-side optimization cost—application servers return faster, and image processing scales independently.

The tradeoff is vendor lock-in and cost structure. Cloudinary charges based on transformation volume, with pricing that exceeds self-hosted optimization at high scale. Teams must calculate the crossover point where CDN costs exceed the infrastructure savings from offloading optimization work. For most applications, this threshold sits around 5-10M monthly transformations.

The alternative approach for self-hosted deployments is a custom loader that points to a dedicated image optimization service running in the same infrastructure:

const customLoader: ImageLoader = ({ src, width, quality }) => {
  const params = new URLSearchParams({
    url: src,
    w: width.toString(),
    q: (quality || 75).toString(),
  })
  
  return `https://images.yourdomain.com/optimize?${params}`
}

This service runs sharp or libvips directly, providing the same optimization capabilities as the built-in Next.js optimizer but on dedicated infrastructure that can scale horizontally without affecting application servers. The implementation complexity is higher—teams must build the optimization API, configure caching layers, and handle security—but it preserves format flexibility and avoids vendor dependencies.

The decision point is simple: if image optimization consumes more than 15% of application server CPU during peak traffic, migrate to a dedicated solution. Below that threshold, the built-in optimizer's simplicity outweighs the operational overhead of custom infrastructure.

Cache Control and Disk Management: maximumDiskCacheSize and contentDispositionType

Next.js caches optimized images in .next/cache/images on disk. Left unconfigured, it checks available disk space once at startup and uses 50% of that as its cache budget, which is reasonable on a fresh instance but risky on a long-running production instance where other disk use grows over time.

%% alt: Cache lifecycle showing disk limit enforcement
flowchart LR
    A("Image optimization request") --> B("Check disk cache")
    B --> C{"Cache hit?"}
    C -->|Yes| D("Serve cached image")
    C -->|No| E("Encode new variant")
    E --> F{"Cache size exceeds limit?"}
    F -->|Yes| G("Evict oldest entries")
    F -->|No| H("Write to cache")
    G --> H
    H --> D
    style G stroke:#fbbf24,fill:#3a2f0b,color:#fef3c7
    style D stroke:#34d399,fill:#0b3b2e,color:#d1fae5
Cache lifecycle showing disk limit enforcement

The maximumDiskCacheSize configuration prevents this failure mode:

import type { NextConfig } from 'next'
 
const config: NextConfig = {
  images: {
    minimumCacheTTL: 60,
    // Limit disk cache to 500MB explicitly (default: 50% of available disk space at startup)
    // New in Next.js 16.1
    maximumDiskCacheSize: 500 * 1024 * 1024,
    formats: ['image/avif', 'image/webp'],
  },
}
 
export default config

When the cache directory exceeds 500MB, Next.js evicts the least-recently-used entries until size drops below the limit. This prevents disk exhaustion but introduces a subtle failure mode: high-traffic applications serving diverse image content may thrash the cache, evicting entries that will be requested again soon. The symptom is elevated encoding latency as popular images are re-optimized repeatedly.

The solution is right-sizing the cache limit based on actual image diversity. Applications serving a fixed set of product images (e-commerce) can use smaller limits because the working set stabilizes. Content platforms with user-generated uploads require larger limits or must offload optimization to a CDN that provides effectively unlimited cache capacity.

The related configuration that teams overlook is contentDispositionType, which controls the Content-Disposition header on optimized images:

const config: NextConfig = {
  images: {
    contentDispositionType: 'inline', // Explicitly opt into inline display
    // Default is 'attachment', which forces a download prompt
  },
}

The default value is attachment, which forces the browser to download the image when the optimized URL is visited directly, added protection since the optimization API can serve arbitrary remote images. Applications that want images to render inline in pages (the common case) must explicitly set contentDispositionType: 'inline'. The failure mode runs opposite to what teams expect: assuming inline is the default and leaving it unset can surprise users with a download prompt when an image URL is opened directly.

The cache behavior interacts with the minimumCacheTTL setting, which controls how long Next.js caches optimized images before revalidating. The default is 14400 seconds (4 hours), which is already reasonable for most applications, but content that rarely changes should push it further to maximize cache hit rates:

const config: NextConfig = {
  images: {
    minimumCacheTTL: 31536000, // 1 year for immutable images
    deviceSizes: [640, 750, 828, 1080, 1200, 1920],
  },
}

Images with cache-busting parameters (query strings or hashed filenames) can use year-long TTLs safely. This eliminates redundant revalidation and maximizes cache hit rates. Applications serving dynamic images that update frequently (user avatars, real-time chart snapshots) must balance cache TTL against content freshness requirements.

Common Pitfalls: priority vs preload, sizes Misconfiguration, and Missing Dimensions

The priority prop on Image components marks images for eager loading but does not automatically inject preload links in the document head, requiring manual configuration in layouts.

%% alt: Priority image loading flow showing manual preload requirement
flowchart LR
    A("Image with priority prop") --> B{"Preload in layout?"}
    B -->|No| C("Browser discovers during render")
    C --> D("Delayed load start")
    B -->|Yes| E("Preload link in head")
    E --> F("Load starts immediately")
    style D stroke:#fbbf24,fill:#3a2f0b,color:#fef3c7
    style F stroke:#34d399,fill:#0b3b2e,color:#d1fae5
Priority image loading flow showing manual preload requirement

Teams mark hero images with priority={true} expecting immediate load initiation, but the browser doesn't discover the image until React hydration completes. The correct approach adds explicit preload links in the root layout:

// app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'Your App',
}
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <head>
        <link
          rel="preload"
          as="image"
          href="/_next/image?url=/hero.jpg&w=1920&q=75"
          imageSrcSet="/_next/image?url=/hero.jpg&w=640&q=75 640w, /_next/image?url=/hero.jpg&w=1920&q=75 1920w"
          imageSizes="100vw"
        />
      </head>
      <body>{children}</body>
    </html>
  )
}

This initiates the hero image load in parallel with HTML parsing, eliminating the discovery delay. The priority prop still matters—it prevents lazy loading and ensures the image isn't deferred—but the preload link provides the actual performance benefit for above-fold content.

The second common pitfall is sizes attribute misconfiguration. The sizes prop tells the browser which image variant to select based on viewport width:

<Image
  src="/product.jpg"
  alt="Product"
  width={1200}
  height={800}
  sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>

When sizes is omitted or incorrect, the browser selects the largest available variant regardless of actual display size. A product thumbnail displayed at 300px width will load the 1920px variant, wasting bandwidth. The failure mode is subtle—images render correctly, but transfer sizes are 3-5x larger than necessary.

The correct approach is auditing actual image display sizes in production using browser DevTools and configuring sizes to match. The syntax accepts CSS media queries and viewport-relative units:

  • 100vw — full viewport width (mobile hero images)
  • 50vw — half viewport width (two-column layouts)
  • (max-width: 768px) 100vw, 33vw — full width on mobile, one-third on desktop

The third pitfall is omitting width and height props, which causes layout shift as images load. Next.js requires dimensions for proper aspect ratio calculation:

// Incorrect - causes layout shift
<Image src="/product.jpg" alt="Product" />
 
// Correct - reserves space, prevents shift
<Image
  src="/product.jpg"
  alt="Product"
  width={1200}
  height={800}
/>

For images with unknown dimensions, use fill mode with a positioned container:

<div style={{ position: 'relative', width: '100%', height: '400px' }}>
  <Image
    src="/dynamic.jpg"
    alt="Dynamic"
    fill
    style={{ objectFit: 'cover' }}
  />
</div>

This approach works for user-generated content where dimensions aren't known at build time. The container reserves space, preventing layout shift, and objectFit controls how the image fills the container.

Frequently Asked Questions

When should teams prioritize AVIF over WebP in production?

Enable AVIF when analytics show 95%+ browser support and cache hit rates exceed 80%. Below these thresholds, the encoding cost outweighs bandwidth savings.

How does remotePatterns differ from the deprecated domains configuration?

remotePatterns enforces protocol, hostname, and pathname matching for security, while domains accepted any path on approved hostnames. Migrate existing domains entries to explicit remotePatterns with pathname wildcards.

What causes disk cache exhaustion in Next.js image optimization?

The default configuration has no maximumDiskCacheSize limit. Set an explicit limit based on available disk space and image diversity to prevent production failures.

Why do images with priority still load slowly?

The priority prop prevents lazy loading but doesn't inject preload links. Add explicit <link rel="preload"> tags in layouts for above-fold images to trigger immediate load initiation.

How should sizes be configured for responsive layouts?

Audit actual display widths in DevTools and configure sizes with media queries matching your breakpoints. Use viewport-relative units (vw) for fluid layouts and fixed pixel values for constrained containers.

Conclusion: A 2026 Image Optimization Checklist

That covers the essential patterns for Next.js image optimization in 2026. Apply these in production and the difference will be immediate:

  1. Set explicit formats arrays in next.config.ts to control AVIF adoption based on browser analytics.
  2. Migrate domains to remotePatterns with protocol and pathname matching before the deprecation becomes a breaking change.
  3. Configure maximumDiskCacheSize to prevent disk exhaustion on long-running instances.
  4. Benchmark AVIF quality settings 10-15 points lower than WebP equivalents to maximize compression without quality loss.
  5. Add preload links in root layouts for priority images to eliminate discovery delays.
  6. Audit and configure sizes attributes based on actual display widths to prevent oversized variant selection.
  7. Consider custom loaders when image optimization exceeds 15% of application CPU usage.

The Next.js image component remains the most accessible optimization solution for modern web applications, but its current defaults and configuration surface require deliberate choices that match infrastructure reality. Teams that treat image optimization as a configuration exercise rather than an automatic feature gain measurable performance improvements without the operational complexity of dedicated CDN services.