CSS FIELD NOTES · 7 MIN READ

font-display explained: swap, fallback, optional and block

What each font-display value does while a web font loads, how it affects invisible text, layout shift and LCP, and how to choose the right one per font.

font-display is an @font-face descriptor that tells the browser what to show while a web font is still downloading. swap shows fallback text at once and switches whenever the font arrives. fallback does the same but only switches if the font arrives quickly. optional uses the web font only if it is available almost immediately. block hides the text for a short time first. Use swap or fallback for body text and optional when layout stability matters most.

The problem: text that waits for a file

A web font is a download. The browser finds out it needs the file only after it has parsed the CSS and found text that uses the family, so the request starts late. For a moment the browser has words to draw and no font to draw them with. It has two options, and both have names.

  • FOIT, flash of invisible text. The browser reserves the space and keeps the text hidden until the font arrives. The page looks finished except that the words are missing.
  • FOUT, flash of unstyled text. The browser draws the text in a fallback font straight away and replaces it when the web font arrives. Readers can start reading, and then the text changes under them.

Left alone, browsers lean toward hiding. According to web.dev, Chromium and Firefox hold text back for up to three seconds when a web font has not loaded. On a slow connection that is three seconds of a blank headline. font-display lets you make that decision yourself, per font face.

The timeline: block, swap and failure periods

The specification describes font loading as a timeline with three periods. The clock starts when the browser first tries to use the font.

  1. Block period. If the font is not loaded yet, text is drawn with an invisible fallback face. It takes up space but cannot be seen. If the font arrives during this period it is used normally.
  2. Swap period. If the font is still not loaded, text is drawn with a visible fallback. If the font arrives during this period, the browser swaps it in.
  3. Failure period. If the font has still not arrived, the browser treats the load as failed and keeps the fallback. No swap happens after this point.

Every font-display value is simply a different choice of how long the first two periods last.

ValueBlock periodSwap periodWhat the reader sees
autoBrowser decidesBrowser decidesUsually close to block
blockShortInfiniteHidden text, then the web font whenever it arrives
swapExtremely smallInfiniteFallback at once, web font whenever it arrives
fallbackExtremely smallShortFallback at once, web font only if it arrives soon
optionalExtremely smallNoneWeb font only if it is ready almost immediately
Period lengths as defined on MDN and in the CSS Fonts specification.

The specification leaves exact durations to browsers. As a guide, web.dev describes the short block period of block as two to three seconds, the extremely small block period as about 100 milliseconds or less, and the short swap period of fallback as about three seconds.

The values in practice

font-display is a descriptor. It goes inside the @font-face rule, not on an element, and it is set separately for every face. A family with four weights has four rules, and each can behave differently.

@font-face {
  font-family: "Literata";
  src: url("/fonts/literata-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

body {
  font-family: "Literata", Georgia, serif;
}
The descriptor lives in @font-face. The fallback that shows during the swap period is the next family in the stack.

swap

Text is readable as early as possible and the web font always wins in the end, even if it takes ten seconds. The cost is that the swap can happen late, after someone has started reading, and the change in letter widths can reflow paragraphs.

fallback

A compromise. Readers on a fast connection get the web font after a brief flash of the fallback. Readers on a slow connection keep the fallback for this page view, and the font file finishes downloading into the cache, so the next page usually has it from the start. Nobody gets a swap in the middle of a paragraph.

optional

The strictest choice. If the font is not available within the tiny block period, the page uses the fallback for the whole visit. The browser may still fetch the file in the background for next time, and on a very slow connection it may decide not to fetch it at all. Because there is no swap period, there is no late swap and no layout shift from the font.

block

Hidden text for a short time, then a swap whenever the font arrives. It has one legitimate use: fonts where a fallback would be meaningless or wrong, such as an icon font, where the fallback would show unrelated letters or empty boxes.

What it means for Core Web Vitals

Two of the Core Web Vitals are sensitive to font loading. The effects are easy to reason about without numbers.

  • Largest Contentful Paint. When the largest element in the viewport is a block of text, hidden text delays it. A long block period can hold LCP back until the font arrives. swap, fallback and optional all show text almost at once, so they remove that delay.
  • Cumulative Layout Shift. A swap changes glyph widths and sometimes line heights. Lines rewrap, the block changes height, and content below it moves. That movement counts toward CLS. optional avoids it completely. swap is the most exposed, because its swap can come at any time.

So the value that is best for LCP on a slow connection, swap, is also the one most likely to cost you CLS. The way out is to make the swap harmless. If the fallback font occupies the same space as the web font, the swap changes letter shapes and nothing moves. That is what the size-adjust, ascent-override, descent-override and line-gap-override descriptors are for. The technique is covered in how to prevent layout shift from web fonts, and choosing the fallback itself in CSS font stacks and fallbacks.

Which value to choose

SituationChooseReason
Body text, brand font mattersswap with a metric-matched fallbackText is readable at once and the brand font always appears
Body text, stability matters more than the fontfallbackNo late swaps, and repeat views get the font from cache
Performance-critical pages, fonts as a nice-to-haveoptional, plus preloadNo font-related layout shift at all
Headline or logo-like display faceswapThe design depends on it, and the text block is short
Icon fontsblockA fallback would show the wrong characters
AnythingNot autoMake the decision yourself instead of inheriting a default

You can mix values. A common setup is swap for the regular weight that carries body text and optional for secondary faces that appear below the fold. If the font is a variable font, one file and one rule cover every weight, which keeps this simple.

Google Fonts, preloading and self-hosting

With Google Fonts you do not write the @font-face rules, so you cannot add the descriptor by hand. The CSS API takes it as a display parameter in the URL instead. The accepted values are the same five: auto, block, swap, fallback and optional. Google’s embed code includes display=swap by default.

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet"
      href="https://fonts.googleapis.com/css2?family=Crimson+Pro:wght@400;700&display=swap">
The display parameter adds font-display to every @font-face rule Google returns.

When you self-host, you control the rule and can also preload the file. A preload tells the browser about the font in the HTML, before it has read any CSS, so the download starts earlier. An early download makes every font-display value behave better: swaps happen sooner, and optional fonts make their deadline more often.

<link rel="preload" href="/fonts/literata-regular.woff2"
      as="font" type="font/woff2" crossorigin>
The crossorigin attribute is required even for same-origin fonts. Without it the file is fetched twice.

Preload only the one or two files that style text at the top of the page. Every preload competes with other critical resources. The @font-face generator writes the rule with the descriptor included, and self-hosting Google Fonts explains how to move the files to your own server.

How to check what a page is really showing

With fallback and optional, the fallback font can stay on screen for a whole visit, by design. With swap, a failed download leaves it there permanently. Either way the CSS still names the web font, so a glance at font-family tells you nothing.

  1. In Chrome or Edge, right-click the text, choose Inspect, open the Computed tab and scroll to Rendered Fonts at the bottom. It names the font actually in use and says whether it is a network resource or a local file. A local file where you expected your web font means the fallback is showing.
  2. To watch the loading behavior, open the Network panel, tick the option to disable the cache, choose a slow throttling preset and reload. You will see the block period or the swap happen.
  3. To read the descriptor a site uses, search its CSS for @font-face in the Sources panel. Computed styles do not show it, because it belongs to the font face and not to an element.

Font Inspector makes the first check a single click. The panel shows the declared family next to the font actually rendered, and raises a fallback flag when they differ. Fonts on this page carries the same warning for every family at once, which is a quick way to confirm a font strategy is doing what you intended.

Know when the fallback is showing.

Font Inspector names the font actually rendered for any text and flags it when it is not the one the CSS asked for. One click, no DevTools digging. Free, no account.

Add to Chrome

Questions & answers

What does font-display: swap do?

It tells the browser to draw text in a fallback font immediately and replace it with the web font whenever that finishes loading. Text is never hidden, but the late replacement can cause a visible change and a layout shift.

What is the difference between font-display swap and fallback?

Both show fallback text almost at once. swap will switch to the web font no matter how late it arrives. fallback only switches during a short window, and otherwise keeps the fallback font for that page view.

Which font-display value is best for Core Web Vitals?

optional is the safest for layout stability because it never swaps late. swap paired with a fallback font whose metrics match the web font gives fast text rendering with very little shift, and always shows the web font.

What is the difference between FOIT and FOUT?

FOIT is a flash of invisible text: the browser hides text until the web font loads. FOUT is a flash of unstyled text: the browser shows a fallback font first and then swaps in the web font.

How do I set font-display for Google Fonts?

Add the display parameter to the stylesheet URL, for example &display=swap. Google then includes that font-display value in every @font-face rule it returns.

Related free tools: @font-face generator · CSS font stack builder

See it on real sites: What font does Instagram use? · What font does Medium use? · What font does Substack use? · What font does Amazon use?

Sources and further reading: MDN: font-display · web.dev: Best practices for fonts · web.dev: Optimize Cumulative Layout Shift · Google Fonts: CSS API update (the display parameter) · MDN: rel=preload

How to prevent layout shift from web fonts

Why a font swap moves layout, how to measure it, and how to stop it with size-adjust, metric overrides, font-display: optional, preloading and smaller files.

Why your font looks different: CSS font stacks and fallbacks explained

Learn how browsers choose a font from your CSS stack, why web fonts fail to load, how to debug it in DevTools, and how to build a fallback that barely shifts.

Font weight numbers and names: 100 to 900 explained

What each font-weight number from 100 to 900 is called, how normal, bold, bolder and lighter resolve, and what the browser does when a weight is missing.