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.
- 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.
- 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.
- 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.
| Value | Block period | Swap period | What the reader sees |
|---|---|---|---|
auto | Browser decides | Browser decides | Usually close to block |
block | Short | Infinite | Hidden text, then the web font whenever it arrives |
swap | Extremely small | Infinite | Fallback at once, web font whenever it arrives |
fallback | Extremely small | Short | Fallback at once, web font only if it arrives soon |
optional | Extremely small | None | Web font only if it is ready almost immediately |
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;
}@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,fallbackandoptionalall 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.
optionalavoids it completely.swapis 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
| Situation | Choose | Reason |
|---|---|---|
| Body text, brand font matters | swap with a metric-matched fallback | Text is readable at once and the brand font always appears |
| Body text, stability matters more than the font | fallback | No late swaps, and repeat views get the font from cache |
| Performance-critical pages, fonts as a nice-to-have | optional, plus preload | No font-related layout shift at all |
| Headline or logo-like display face | swap | The design depends on it, and the text block is short |
| Icon fonts | block | A fallback would show the wrong characters |
| Anything | Not auto | Make 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">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>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.
- 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.
- 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.
- To read the descriptor a site uses, search its CSS for
@font-facein 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.
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