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.
Deferring and prioritizing images
Section titled “Deferring and prioritizing images”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.
Where it is supported
Section titled “Where it is supported”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>Feature detection and fallback
Section titled “Feature detection and fallback”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;}Practical checklist
Section titled “Practical checklist”- 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
widthandheighton every image, especiallyloading="lazy"ones, so the browser has a known layout size and the image can still be found and loaded. - Per MDN,
fetchpriorityprovides a relative-priority hint to the browser rather than a guarantee, so reservehighfor the images that matter most. - Per MDN,
font-display’s default (auto) leaves the block behavior up to the browser; setswaporoptionalexplicitly if you want the extremely small block period those values define instead. - See Resource hints for
rel="preload".
Where to go next
Section titled “Where to go next”Specifications
| Specification | Status |
|---|---|
| None. | |