Optimise AVIF images for fast page loads. Practical guide to quality settings, responsive delivery, lazy loading, and Core Web Vitals.
Shipping AVIF That Actually Loads Fast
AVIF saves bytes only when encoding, sizing, fallback, and load timing all work together. This guide is the end-to-end workflow, from quality target to Core Web Vitals proof.
The format does the heavy lifting on compression — a photo is ~50% lighter than JPEG and 20–25% lighter than WebP at matched quality. The rest is delivery. Start a conversion now with Convert JPG to AVIF or Convert PNG to AVIF, reverse it with Convert AVIF to JPG and Convert AVIF to PNG, or read the AVIF format overview first.
The optimisation workflow at a glance
A reliable AVIF rollout runs in seven steps, in order:
- Set a quality target — pick a quantizer for the content type.
- Generate responsive widths — produce 4–5 sizes per image.
- Wire the fallback —
<picture>with AVIF → WebP → JPEG. - Prioritise the LCP image — eager,
fetchpriority="high", preloaded. - Lazy-load the rest — defer everything below the fold.
- Serve through a CDN — content-negotiated, cached, immutable.
- Measure — confirm LCP under 2.5s with field data.
Each step assumes the previous one. A flawless quantizer on a 4000px file sent to a phone is still slow; a perfectly sized hero that lazy-loads still wrecks Largest Contentful Paint.
Step 1 — Pick a quality target
For web photography, target an AVIF quantizer of roughly 50–65 (libavif scale, where lower means higher quality). That band is the documented sweet spot for 2025–2026 web delivery, balancing artefact-free output against minimum bytes, per web.dev's AVIF guidance.
AVIF's quality scale is not JPEG's. AVIF q≈50 looks roughly like JPEG q=85 or WebP q=78, so reusing a JPEG-era "q=85 to be safe" number produces archival files far heavier than the web needs. Tune to the AVIF scale directly:
| Content type | Quantizer (q) | Chroma | Notes |
|---|---|---|---|
| Hero / above-the-fold | 55–63 | 4:2:0 | Scrutinised on retina; do not push lower |
| Product photography | 50–58 | 4:2:0 | Ecommerce sweet spot |
| Article body images | 48–55 | 4:2:0 | Indistinguishable from source at this size |
| Graphics with text | 60–68 | 4:4:4 | Keep full chroma to protect edges |
| Thumbnails ≤ 200px | 40–50 | 4:2:0 | Aggressive compression is invisible |
Encode photos with 4:2:0 chroma subsampling to halve colour data the eye barely resolves; keep 4:4:4 for screenshots, logos, and anything with crisp coloured text. For the full parameter reference, see AVIF Compression Settings; to decide lossy versus lossless for line art, see Lossy vs Lossless Compression.
# Build-time photo: quality ~62/100, balanced encode speed
avifenc --qcolor 24 --qalpha 24 --speed 6 --yuv 420 input.jpg output.avif
# Graphics with text: higher quality, full chroma
avifenc --qcolor 18 --speed 6 --yuv 444 screenshot.png output.avif
--speed runs 0–10; speed 4–6 is the production balance of encode time against file size. AVIF encodes 5–20× slower than WebP, so this flag matters far more than its cwebp equivalent. Push to --speed 8 only for high-volume on-the-fly jobs.
Step 2 — Generate responsive widths
Sending a 3000px image to a 375px phone wastes bytes no quantizer can recover. Produce 4–5 widths per asset — 400, 800, 1200, 1800, and 2400 for full-bleed heroes — then let the browser choose with srcset and sizes.
import sharp from "sharp";
for (const width of [400, 800, 1200, 1800]) {
await sharp("source.jpg")
.resize({ width, withoutEnlargement: true })
.avif({ quality: 62, effort: 5, chromaSubsampling: "4:2:0" })
.toFile(`hero-${width}.avif`);
}
Sharp's quality: 62 maps to roughly the q≈52 quantizer band; effort runs 0–9 (higher is slower and smaller). Set sizes to match your CSS layout, not the intrinsic file width — an inaccurate sizes is the most common reason a responsive set still serves oversized images.
Step 3 — Wire the AVIF → WebP → JPEG fallback
AVIF reaches ~94% of global browsers in 2026 (caniuse), so the remaining tail needs a fallback. The <picture> element negotiates in source order — the browser takes the first <source> it can decode and falls through to the <img>:
<picture>
<source
type="image/avif"
srcset="/img/hero-800.avif 800w, /img/hero-1200.avif 1200w, /img/hero-1800.avif 1800w"
sizes="(max-width: 768px) 100vw, 1200px"
/>
<source
type="image/webp"
srcset="/img/hero-800.webp 800w, /img/hero-1200.webp 1200w, /img/hero-1800.webp 1800w"
sizes="(max-width: 768px) 100vw, 1200px"
/>
<img
src="/img/hero-1200.jpg"
alt="Descriptive alt text"
width="1200"
height="675"
decoding="async"
/>
</picture>
Order matters: AVIF first, WebP second, JPEG last. WebP covers near-100% of the tiny AVIF gap at a still-respectable size; JPEG is the universal floor. Always set width and height on the <img> so the browser reserves layout space and avoids Cumulative Layout Shift. For the support matrix and edge cases, see AVIF Browser Support; for the fallback formats themselves, see WebP and JPG.
Step 4 — Prioritise the LCP image
One image on each page is the Largest Contentful Paint element — usually the hero or first product photo. Load it eagerly with fetchpriority="high" and never lazy-load it. Lazy-loading the LCP image is the single most damaging LCP mistake, yet only 17% of sites apply fetchpriority to the element that needs it.
<img src="/img/hero-1200.avif" alt="…" fetchpriority="high" decoding="async"
width="1200" height="675" />
For a hero defined late in the markup or set via CSS, preload it from <head> so discovery does not wait on parsing:
<link
rel="preload"
as="image"
type="image/avif"
href="/img/hero-1200.avif"
imagesrcset="/img/hero-800.avif 800w, /img/hero-1200.avif 1200w"
imagesizes="100vw"
fetchpriority="high"
/>
Preload only the LCP image — preloading several puts them in contention and slows the one that decides your score. fetchpriority is supported across Chrome 102+, Safari 17.2+, Firefox 132+, and Edge 102+, so it is safe in production. Adding it and removing lazy-loading from the hero alone improves LCP by 200–500ms on many sites.
Step 5 — Lazy-load everything below the fold
Every image that starts off-screen should defer its request until the user scrolls near it:
<img src="/img/figure-800.avif" alt="…" loading="lazy" decoding="async"
width="800" height="450" />
loading="lazy" skips the network request until the image approaches the viewport; decoding="async" keeps decode work off the critical rendering path, which protects Interaction to Next Paint (INP) on image-heavy pages. The rule is binary: eager for the LCP image, lazy for all the rest. For nuance on IntersectionObserver, placeholders, and framework integration, see AVIF Lazy Loading, and for React/Next.js specifics see AVIF in React.
Step 6 — Serve through a CDN
Delivery is where good encoding either reaches the user fast or stalls. Three layers matter.
Negotiate by Accept header. Browsers that decode AVIF send Accept: image/avif,…. A CDN can return AVIF to them and a fallback to everyone else at the same URL — provided it sets Vary: Accept so caches store the variants separately.
Cache immutably. Hashed, content-addressed filenames can be cached for a year:
Cache-Control: public, max-age=31536000, immutable
Vary: Accept
Because the URL changes when the file changes, never overwrite an AVIF in place under a long max-age — stale clients will hold the old bytes. Rename or re-hash on every re-encode.
Choose encode-once or on-the-fly. This is the central delivery tradeoff:
| Approach | Best for | Cost |
|---|---|---|
| Encode once | Heroes, product shots, fixed sets | Build time; you own the pipeline |
| On-the-fly | Large or user-generated libraries | First-request latency; pay-per-transform |
Encode-once at low --speed gives the smallest files for assets you control. On-the-fly CDNs — Cloudflare Polish, Cloudinary, Imgix, and Vercel's image optimisation — accept a JPEG or PNG original and emit a cached AVIF per Accept, which is the fastest path when you cannot retool the build. Serving AVIF over HTTP/3 is the quickest transport; most CDNs enable it by default.
Step 7 — Measure the impact
Optimisation without measurement is guesswork. Judge an AVIF rollout against the 2026 Core Web Vitals thresholds, measured at the 75th percentile of real users:
| Metric | Target | Image relevance |
|---|---|---|
| Largest Contentful Paint (LCP) | < 2.5s | The hero is usually the LCP element |
| Cumulative Layout Shift (CLS) | < 0.1 | Missing width/height is the top cause |
| Interaction to Next Paint (INP) | < 200ms | Off-thread decode (decoding="async") helps |
Capture field data with the Chrome User Experience Report or any real-user-monitoring tool; in development, run Lighthouse and WebPageTest. Confirm AVIF is actually being served (not the JPEG fallback) by checking the response Content-Type in DevTools. For the complete measurement framework, see Core Web Vitals & Images, and for crawl-and-rank considerations see Image SEO Best Practices.
The optimisation checklist
Run this before shipping any image-heavy page:
- Quantizer set per content type (q≈50–65 for photos, 4:2:0 chroma).
- 4–5 responsive widths generated per asset.
-
sizesmatches the real CSS layout, not the file width. -
<picture>orders AVIF → WebP → JPEG with an<img>fallback. -
widthandheightset on every<img>. - LCP image eager,
fetchpriority="high", never lazy-loaded. - Exactly one image preloaded (the LCP image).
- All below-fold images
loading="lazy"+decoding="async". -
Cache-Control: …immutableplusVary: Accepton served variants. - Field LCP under 2.5s, CLS under 0.1, AVIF confirmed in
Content-Type.
Frequently asked questions
What AVIF quality should I use for the web?
Use a quantizer of roughly 50–65 (q≈50–60/100 in encoders like Sharp). That is artefact-free for photos while staying far lighter than JPEG q=85.
Why does my AVIF look worse than the JPEG it replaced?
You stacked two lossy passes at too low a quality. Re-encoding a lossy JPEG into AVIF compounds loss — encode at q≈50 or let a CDN transform the original.
Should AVIF heroes be lazy-loaded?
No. The LCP image must load eagerly with fetchpriority="high"; lazy-loading it is the most common cause of a failing LCP.
Do I still need a fallback if AVIF support is 94%?
Yes. A <picture> with WebP and JPEG fallbacks covers the remaining ~6% — iOS 15-and-older and some locked-down browsers — at no cost to modern clients.
Is on-the-fly CDN conversion as good as encoding myself?
Almost. Purpose-built encodes at low --speed compress slightly better, but a CDN's cached, Accept-negotiated output is faster to deploy and good enough for most libraries.
Where to go next
- Convert your library: JPG to AVIF, PNG to AVIF.
- Compare before you commit: AVIF vs WebP, AVIF vs JPG.
- Drill into one lever: AVIF Compression Settings, AVIF Lazy Loading, Lossy vs Lossless Compression.
- Tie it to metrics and crawling: Core Web Vitals & Images, Image SEO Best Practices.