How font-display: swap Eliminates FOIT

Web fonts significantly enhance visual design, but their loading behavior can compromise user experience through the Flash of Invisible Text (FOIT). When a browser downloads a custom web font, it traditionally hides the text until the file is completely retrieved. The CSS font-display: swap property solves this issue by altering the browser's font-rendering lifecycle, forcing it to immediately display a fallback system font and then swap in the custom web font as soon as it finishes downloading.

Understanding FOIT

FOIT occurs when a browser detects that an element uses an external web font that is not yet downloaded and cached. By default, major browsers like Chrome, Firefox, and Safari hold back text rendering—displaying blank, invisible space—while waiting for the font file. This block period typically lasts up to three seconds. If a user is on a slow or unstable network connection, they are left staring at an empty page despite the text content already being present in the HTML. This delays readability, harms perceived performance, and negatively impacts Core Web Vitals, specifically Largest Contentful Paint (LCP).

The Font Display Timeline

To understand how swap fixes FOIT, it helps to examine how the browser handles custom fonts using three distinct time windows:

  1. Font Block Period: The time during which the browser waits for the font to load, rendering text invisibly.
  2. Font Swap Period: The time during which the browser renders a fallback font but will replace it if the custom font finishes downloading.
  3. Font Failure Period: If the font has not loaded by this point, the browser treats the load as failed and permanently uses the fallback font.

How font-display: swap Works

Applying font-display: swap directly alters these time periods by setting the Font Block Period to 0ms and giving the Font Swap Period an infinite duration.

Because the block period is eliminated entirely, the browser never hides the text. Instead, it follows this sequence:

  1. Immediate Rendering: The browser immediately renders the text using the first available system fallback font listed in your CSS font-family stack.
  2. Background Loading: The external web font continues to download asynchronously in the background without holding up the rest of the page rendering.
  3. The Swap: As soon as the custom font file becomes available, the browser re-renders the text using the newly loaded font.

Implementation

You can implement font-display: swap directly inside the @font-face rule in your stylesheet:

@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  src: url('/fonts/open-sans.woff2') format('woff2');
  font-display: swap;
}

body {
  font-family: 'Open Sans', Arial, sans-serif;
}

If you load fonts via a third-party service like Google Fonts, you can include display=swap as a query parameter in the stylesheet link URL:

<link href="https://fonts.googleapis.com/css2?family=Open+Sans&display=swap" rel="stylesheet">

Trade-offs: FOIT vs. FOUT

While font-display: swap completely eliminates the Flash of Invisible Text, it introduces the Flash of Unstyled Text (FOUT). With FOUT, users briefly see content rendered in a basic fallback font (like Arial or Times New Roman) before it shifts to the custom web font.

From a user experience and accessibility perspective, FOUT is almost universally preferred over FOIT. Readable content delivered instantly is far more valuable than an aesthetically complete layout that takes seconds to appear. To minimize visual disruption during the swap, developers can match the fallback font's metrics (using CSS properties like size-adjust, ascent-override, and descent-override) to ensure the transition does not cause noticeable layout shifts (CLS).