Skip to content

Web Development · Performance

Web Images in 2026: AVIF, WebP, and the LCP Work Nobody Does Until It's a Problem

Images are the single biggest factor in Largest Contentful Paint for most sites. AVIF has widespread browser support now. Here's the optimization stack worth using and how to implement it.

Anurag Verma

Anurag Verma

7 min read

Web Images in 2026: AVIF, WebP, and the LCP Work Nobody Does Until It's a Problem

Sponsored

Share

Check your site’s LCP on a mobile connection and there’s a reasonable chance an image is the culprit. Not JavaScript. Not fonts. An unoptimized JPEG or PNG that nobody revisited after it was uploaded two years ago.

Image optimization isn’t technically hard. The formats have been stable for years, the tooling is good, and browser support has caught up. What gets in the way is that it’s easy to deprioritize, because images work. They’re just slow, and slow doesn’t fail visibly the way a broken API call does. LCP scores sit in the yellow until someone decides to fix them.

This is the setup worth putting in place, and it’s not that much work.

Where Browser Support Actually Stands in 2026

WebP has been supported everywhere since 2020. Safari added support in version 14. There is no meaningful browser population that doesn’t support WebP. If you’re still serving JPEG by default, you’re leaving compression on the table for every user.

AVIF (AV1 Image File Format) has supported in Chrome since 2020, Firefox since 2021, and Safari since version 16 (2022). Global browser support is above 90%. The holdout was Safari, and it’s been more than three years since that changed.

AVIF produces smaller files than WebP at the same visual quality, especially for photos with complex detail. The encoding is slower (AVIF encoding can take 10-20x longer than WebP encoding), but this is a build-time concern, not a serving concern. For CDN-delivered content, you encode once and serve many times.

Format Comparison for Common Image Types

Image typeRecommended formatNotes
Photos, hero imagesAVIF, fallback WebPBest compression ratios
Product imagesAVIF, fallback WebPAlso handles transparency
UI icons, logosSVG (preferred), WebPVector is usually better
Screenshots, diagramsWebP or PNGPNG for exact pixel accuracy
Animated imagesWebP or AVIFBoth support animation; prefer over GIF
Open Graph / socialJPEG or WebPSome crawlers still prefer JPEG

Serving Multiple Formats with <picture>

The browser picks the first source it supports:

<picture>
  <source srcset="/images/hero.avif" type="image/avif" />
  <source srcset="/images/hero.webp" type="image/webp" />
  <img
    src="/images/hero.jpg"
    alt="Team working at a conference table"
    width="1200"
    height="630"
    loading="lazy"
    decoding="async"
  />
</picture>

AVIF is listed first (modern browsers take it), WebP as the next choice, JPEG as the final fallback for very old clients. The <img> tag at the bottom is required: it’s the fallback and carries the alt, width, height, loading, and decoding attributes.

Responsive Images with srcset

Serving a 2400px image to a 375px phone is another common waste. srcset lets the browser pick the appropriate size:

<picture>
  <source
    type="image/avif"
    srcset="
      /images/hero-480.avif   480w,
      /images/hero-768.avif   768w,
      /images/hero-1200.avif 1200w,
      /images/hero-2400.avif 2400w
    "
    sizes="(max-width: 480px) 480px, (max-width: 768px) 768px, 1200px"
  />
  <source
    type="image/webp"
    srcset="
      /images/hero-480.webp   480w,
      /images/hero-768.webp   768w,
      /images/hero-1200.webp 1200w,
      /images/hero-2400.webp 2400w
    "
    sizes="(max-width: 480px) 480px, (max-width: 768px) 768px, 1200px"
  />
  <img
    src="/images/hero-1200.jpg"
    alt="Team working at a conference table"
    width="1200"
    height="630"
  />
</picture>

The sizes attribute tells the browser how wide the image will be rendered at different viewport widths. Combined with srcset, the browser multiplies the display size by the device pixel ratio and fetches the closest match. A 375px device at 3x DPR fetches the 1200px variant, not the 2400px one.

Generating Multiple Formats at Build Time

With Sharp (Node.js)

Sharp is the standard Node image processing library. Fast, minimal dependencies, good output quality.

import sharp from "sharp";
import { readdir, mkdir } from "fs/promises";
import path from "path";

const INPUT_DIR = "public/images/source";
const OUTPUT_DIR = "public/images/optimized";

const WIDTHS = [480, 768, 1200, 2400];
const QUALITY = { avif: 60, webp: 80, jpg: 85 };

async function processImage(inputPath: string, baseName: string) {
  const img = sharp(inputPath);
  const { width: originalWidth } = await img.metadata();

  for (const width of WIDTHS) {
    if (originalWidth && width > originalWidth) continue;

    const base = `${baseName}-${width}`;

    await img.clone().resize(width).avif({ quality: QUALITY.avif }).toFile(
      path.join(OUTPUT_DIR, `${base}.avif`)
    );

    await img.clone().resize(width).webp({ quality: QUALITY.webp }).toFile(
      path.join(OUTPUT_DIR, `${base}.webp`)
    );

    await img.clone().resize(width).jpeg({ quality: QUALITY.jpg, mozjpeg: true }).toFile(
      path.join(OUTPUT_DIR, `${base}.jpg`)
    );
  }
}

Run this as a build step. For content sites with many images, add incremental processing: track which source files have changed and only reprocess those.

With Squoosh CLI

Squoosh’s CLI is useful for batch encoding when you want to tune quality settings interactively first:

npx @squoosh/cli --avif '{"quality": 60}' --webp '{"quality": 80}' -d output/ images/*.jpg

Framework-Level Optimization

If you’re using Next.js, the next/image component handles format selection, responsive srcsets, and lazy loading automatically. It converts to WebP or AVIF on demand and caches the results.

import Image from "next/image";

export function HeroImage() {
  return (
    <Image
      src="/images/hero.jpg"
      alt="Team working at a conference table"
      width={1200}
      height={630}
      priority  // removes lazy loading for LCP images
      quality={80}
    />
  );
}

For Astro, the <Image> component does the same. For other frameworks, check whether there’s a built-in image component before hand-rolling your own pipeline.

The LCP Image Specifically

For the page’s LCP element (usually the largest above-the-fold image), loading="lazy" is the wrong choice. Lazy loading defers the fetch until the image is near the viewport, which means the LCP image fetch starts late.

For the LCP image:

  • Remove loading="lazy" or use loading="eager"
  • Add fetchpriority="high" to tell the browser to prioritize this fetch
  • Add a <link rel="preload"> in the <head> so the browser discovers it early
<head>
  <link
    rel="preload"
    as="image"
    href="/images/hero-1200.avif"
    imagesrcset="/images/hero-480.avif 480w, /images/hero-1200.avif 1200w"
    imagesizes="(max-width: 480px) 480px, 1200px"
    type="image/avif"
  />
</head>

<body>
  <picture>
    <source
      type="image/avif"
      srcset="/images/hero-480.avif 480w, /images/hero-1200.avif 1200w"
      sizes="(max-width: 480px) 480px, 1200px"
    />
    <img
      src="/images/hero-1200.jpg"
      alt="Team working"
      width="1200"
      height="630"
      fetchpriority="high"
    />
  </picture>
</body>

The preload hint ensures the browser starts fetching the AVIF version before it processes the full HTML. For pages where the LCP image is on the critical path, this alone can meaningfully improve measured LCP.

CDN Delivery

If you’re serving images via a CDN, you can often offload format conversion entirely. Cloudflare Images, Cloudinary, Imgix, and similar services accept a source image URL and return the right format based on the Accept header.

# Cloudinary example: request an image with automatic format selection
https://res.cloudinary.com/demo/image/upload/f_auto,q_auto/sample.jpg

f_auto lets Cloudinary choose AVIF, WebP, or JPEG based on browser support. q_auto adjusts quality based on perceived visual difference. The original source file never changes; the CDN handles serving the right format to each client.

This is the lowest-friction path for sites with user-uploaded content or large existing image catalogs. You don’t need to regenerate files. Just change the delivery URL.

Checking Your Work

After making changes, check your LCP images with:

  • Chrome DevTools Network tab: Look at the image requests. Are they fetching AVIF/WebP? Check the size compared to the original.
  • PageSpeed Insights / Lighthouse: The “Serve images in next-gen formats” and “Properly size images” audits tell you which images still need work.
  • Core Web Vitals report in Search Console: Shows LCP field data from real users, which is what Google actually uses for ranking.

The goal isn’t a perfect Lighthouse score on your dev machine. It’s a good LCP on a real device on a real connection. Run Lighthouse in “mobile” mode with CPU throttling enabled to see what actual users on mid-tier phones experience.

Sponsored

Sponsored

Discussion

Join the conversation.

Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.

Sponsored