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:
- Font Block Period: The time during which the browser waits for the font to load, rendering text invisibly.
- Font Swap Period: The time during which the browser renders a fallback font but will replace it if the custom font finishes downloading.
- 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:
- Immediate Rendering: The browser immediately
renders the text using the first available system fallback font listed
in your CSS
font-familystack. - Background Loading: The external web font continues to download asynchronously in the background without holding up the rest of the page rendering.
- 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).