Skip to content

Performance · Concept

Font and image optimization: font-display, picture, and AVIF/WebP

Published Updated

In one line: the font-display CSS descriptor controls whether text stays invisible or renders in a fallback font while a web font loads, and the <img> element’s loading attribute defers when an image is fetched, fetchpriority hints its relative priority, and formats like AVIF and WebP change how much data that fetch transfers.

Controlling font-loading visibility with font-display

Section titled “Controlling font-loading visibility with font-display”

Per MDN, font-display defines a block period (how long the browser hides text waiting for the font) and a swap period (how long it will still swap in the font once loaded) for a @font-face rule. swap gives an extremely short block period so text renders in a fallback font almost immediately, then swaps once the web font arrives:

@font-face {
font-family: "Brand Sans";
src: url("/fonts/brand-sans.woff2") format("woff2");
font-weight: 400;
font-display: swap;
}

Per MDN, font-display is Baseline widely available, supported since January 2020.

Per MDN, the <img> element’s loading attribute defers loading with lazy until the image is calculated to be near the viewport, while eager (the default) loads immediately; this deferral only applies when JavaScript is enabled, since loading="lazy" has no effect when scripting is disabled. The fetchpriority attribute separately hints the relative priority of the fetch:

<img
src="/images/hero.avif"
width="1200"
height="630"
loading="eager"
fetchpriority="high"
alt="Product hero shot"
/>
<img
src="/images/gallery-3.avif"
width="400"
height="300"
loading="lazy"
alt="Gallery photo 3"
/>

Per MDN, explicit width and height are recommended on every image, and especially so for loading="lazy" ones: an image without them has a 0-by-0 layout size until it loads, and per MDN an unloaded image the browser deems to have no size may never load if it never intersects a visible part of an element.

Per MDN, font-display is Baseline widely available. For images, loading and fetchpriority on <img> are broadly supported in current browsers. AVIF is supported in Chrome, Edge, and Opera, with Firefox adding still-image support in Firefox 93 and Safari adding support in Safari 16.1 — a shorter support history than WebP’s, which MDN describes as supported in all current versions of Chrome, Edge, Firefox, Opera, and Safari, so a format fallback chain still matters for AVIF, especially for users on older browser versions.

Serving AVIF and WebP with a JPEG fallback

Section titled “Serving AVIF and WebP with a JPEG fallback”

Per MDN, AVIF commonly compresses around 50% smaller than JPEG at comparable quality, and WebP’s lossy mode is typically 25-35% smaller than JPEG. <picture> wraps one or more <source> elements alongside a fallback <img>:

<picture>
<source srcset="/images/hero.avif" type="image/avif" />
<source srcset="/images/hero.webp" type="image/webp" />
<img src="/images/hero.jpg" width="1200" height="630" alt="Product hero shot" />
</picture>

Per MDN, HTMLImageElement.decode() returns a promise that resolves once the image is ready to use, or rejects with an EncodingError when decoding fails. This can happen because the image request failed, the request changed after decode() was called, or the image data is corrupted. Code can use a rejection to select its fallback for that attempt, but the rejection alone does not establish that the browser lacks AVIF decoding support:

let avifSupportPromise;
function canDecodeAvifProbe() {
if (avifSupportPromise) return avifSupportPromise;
if (!('Image' in window)) {
avifSupportPromise = Promise.resolve(false); // No Image constructor: skip probing.
return avifSupportPromise;
}
const probe = new Image();
probe.src =
'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQAMAAAAABNjb2xybmNseAACAAIABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAAB9tZGF0EgAKCBgABogQEDQgMgkxIABJSElBQkJB';
avifSupportPromise = probe
.decode()
.then(() => true)
.catch(() => false); // This attempt failed: select the fallback image.
return avifSupportPromise;
}
  • Per MDN, provide an AVIF fallback in WebP, JPEG, or PNG via <picture> or another approach — AVIF requires a full download before anything renders, and its support history across current browsers is shorter than WebP’s.
  • Set width and height on every image, especially loading="lazy" ones, so the browser has a known layout size and the image can still be found and loaded.
  • Per MDN, fetchpriority provides a relative-priority hint to the browser rather than a guarantee, so reserve high for the images that matter most.
  • Per MDN, font-display’s default (auto) leaves the block behavior up to the browser; set swap or optional explicitly if you want the extremely small block period those values define instead.
  • See Resource hints for rel="preload".

Specifications

SpecificationStatus
None.