X Xerobit

AVIF Image Format — Better Compression Than WebP and JPEG

AVIF offers 50% smaller files than JPEG at equivalent quality. Learn browser support, how to convert images to AVIF with sharp and ffmpeg, and how to serve AVIF with WebP and...

Mian Ali Khalid · · 4 min read
Use the tool
Image Compressor
Compress JPEG, PNG, and WebP images in your browser. Adjustable quality, batch mode. Files never leave your device.
Open Image Compressor →

AVIF is the best image format for web in 2024 by compression ratio. A 1MB JPEG typically becomes 350-500KB WebP and 200-350KB AVIF at equal visual quality.

Compress your images with the Image Compressor.

Format comparison

FormatCompressionBrowser supportEncoding speed
JPEGBaseline100%Fast
WebP~30% smaller than JPEG97%+Fast
AVIF~50% smaller than JPEG92%+Slow
AVIF (animated)~60% smaller than GIF92%+Very slow

AVIF browser support (2024): Chrome 85+, Firefox 93+, Safari 16+. IE and older Safari need a fallback.

Convert to AVIF with sharp (Node.js)

import sharp from 'sharp'; // npm install sharp

// Single image conversion:
await sharp('photo.jpg')
  .avif({
    quality: 65,      // 0-100, default 50
    effort: 4,        // 0-9: speed vs compression tradeoff, default 4
    chromaSubsampling: '4:2:0',  // default, reduce for quality images
  })
  .toFile('photo.avif');

// Batch conversion:
import { glob } from 'glob';
import { promises as fs } from 'fs';

const images = await glob('public/images/**/*.{jpg,jpeg,png}');

for (const img of images) {
  const avifPath = img.replace(/\.(jpg|jpeg|png)$/, '.avif');
  const webpPath = img.replace(/\.(jpg|jpeg|png)$/, '.webp');

  await Promise.all([
    sharp(img).avif({ quality: 65 }).toFile(avifPath),
    sharp(img).webp({ quality: 75 }).toFile(webpPath),
  ]);

  const original = (await fs.stat(img)).size;
  const avif = (await fs.stat(avifPath)).size;
  console.log(`${img}: ${(original/1024).toFixed(0)}KB → ${(avif/1024).toFixed(0)}KB AVIF`);
}

Convert with ffmpeg (CLI)

# Single image:
ffmpeg -i photo.jpg -c:v libaom-av1 -crf 30 photo.avif

# CRF scale: 0 (lossless) to 63 (worst), ~30 = good quality
# Lower CRF = better quality + larger file

# Batch convert all JPEGs in directory:
for f in *.jpg; do
  ffmpeg -i "$f" -c:v libaom-av1 -crf 30 "${f%.jpg}.avif"
done

# With speed control (0=slowest/best, 4=balanced, 8=fastest):
ffmpeg -i photo.jpg -c:v libaom-av1 -crf 30 -cpu-used 4 photo.avif

Serve AVIF with HTML picture element

<!-- Browser picks the first format 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="Hero image" width="800" height="450">
</picture>

<!-- With responsive sizes: -->
<picture>
  <source
    srcset="/images/hero-400.avif 400w, /images/hero-800.avif 800w, /images/hero-1200.avif 1200w"
    type="image/avif"
    sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"
  >
  <source
    srcset="/images/hero-400.webp 400w, /images/hero-800.webp 800w, /images/hero-1200.webp 1200w"
    type="image/webp"
    sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"
  >
  <img
    src="/images/hero-800.jpg"
    alt="Hero image"
    width="800"
    height="450"
    loading="lazy"
  >
</picture>

nginx: serve AVIF automatically

# Serve AVIF if the browser accepts it and the file exists:
location ~* \.(jpe?g|png)$ {
  add_header Vary Accept;
  
  # Try AVIF first:
  if ($http_accept ~* "image/avif") {
    rewrite ^(.+)\.(jpe?g|png)$ $1.avif break;
  }
  
  # Fallback to WebP:
  if ($http_accept ~* "image/webp") {
    rewrite ^(.+)\.(jpe?g|png)$ $1.webp break;
  }
}

A cleaner approach uses map directives — but the picture element in HTML gives browsers explicit control and is more maintainable.

AVIF quality settings guide

// Portrait photography (fine detail, skin tones):
sharp('portrait.jpg').avif({ quality: 70, effort: 6 })

// Product images (clean background, sharp edges):
sharp('product.png').avif({ quality: 75, effort: 4 })

// Hero/background images (large, some blur acceptable):
sharp('hero.jpg').avif({ quality: 55, effort: 4 })

// Thumbnails (small, fast loading more important):
sharp('thumb.jpg').avif({ quality: 60, effort: 2 })

// Lossless (icons, UI elements with text):
sharp('ui-element.png').avif({ lossless: true })

Build pipeline integration

Vite:

// vite.config.js
import viteImagemin from 'vite-plugin-imagemin';

export default {
  plugins: [
    viteImagemin({
      avif: { quality: 65 },
      webp: { quality: 75 },
    }),
  ],
};

Next.js (built-in):

// next.config.js — Next.js handles AVIF conversion automatically:
module.exports = {
  images: {
    formats: ['image/avif', 'image/webp'],
    // Next.js serves AVIF to browsers that accept it
  },
};

Related posts

Related tool

Image Compressor

Compress JPEG, PNG, and WebP images in your browser. Adjustable quality, batch mode. Files never leave your device.

Written by Mian Ali Khalid. Part of the Frontend & Design pillar.