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

> How the font-display CSS descriptor controls text visibility while a web font loads, how the img loading and fetchpriority attributes and the picture element control image delivery, and how AVIF and WebP compress smaller than JPEG and PNG per MDN.

**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

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:

```css
@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

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:

```html
<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

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

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>`:

```html
<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

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:

```js
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

- [ ] 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](/reference/performance/resource-hints/) for `rel="preload"`.

## Where to go next

- [Resource hints: rel="preload" and rel="preconnect"](/reference/performance/resource-hints/)
- [Core Web Vitals](/reference/performance/core-web-vitals/)