How to Eliminate FOIT with Font Subsetting and Swap

Flash of Invisible Text (FOIT) degrades user experience and hurts Core Web Vitals by hiding text behind empty space while custom web fonts load. Combining font subsetting with the CSS font-display: swap property provides an effective, two-pronged strategy to eliminate this problem entirely. Subsetting strips out unneeded glyphs to drastically reduce file sizes and accelerate delivery, while font-display: swap tells the browser to immediately display fallback text until the custom font is ready. Together, these techniques ensure that page content remains instantly readable on any connection.

Understanding FOIT

When a browser encounters an external web font declared in CSS, it often delays rendering the affected text until the font file finishes downloading. By default, browsers like Chrome, Firefox, and Safari hold text in an invisible state for up to three seconds. If the network is slow or the font file is large, users are left staring at blank spaces where headlines and body copy should be. This behavior is Flash of Invisible Text.

How font-display: swap Solves the Visibility Problem

The font-display descriptor in the @font-face rule controls how a font is rendered relative to its download progress. Applying the swap value eliminates the initial block period where text is invisible.

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

When set to swap, the browser:

  1. Gives the font face a zero-second block period, meaning text is never hidden.
  2. Instantly renders the text using the next available fallback font in the font stack (e.g., Arial, system-ui).
  3. Downloads the custom font in the background.
  4. Swaps the fallback font with the custom font as soon as the file finishes loading.

This approach converts FOIT into FOUT (Flash of Unstyled Text). While the user may notice a slight visual shift when the font changes, they can read the content immediately, preventing frustration and lowering bounce rates.

How Font Subsetting Minimizes the Swap Duration

While font-display: swap ensures content is readable right away, a noticeable jump between the fallback font and the custom font can be visually jarring and potentially trigger Cumulative Layout Shift (CLS). This is where font subsetting becomes critical.

Standard font files often contain thousands of characters, including multiple language scripts, ligatures, historical symbols, and mathematical glyphs that a website will never use. A full font file can easily exceed 200KB.

Font subsetting is the process of generating a custom, trimmed font file containing only the characters required for your content (for instance, basic Latin characters, numbers, and standard punctuation).

Subsetting reduces font payloads by:

  • Drastically shrinking file sizes: A 150KB font can often be reduced to 15KB–30KB using tools like Glyphhanger, Subfont, or Google Fonts' text query parameter.
  • Accelerating download times: Smaller files transit the network significantly faster, particularly on mobile connections.
  • Shortening the swap delay: Because the file arrives almost immediately, the transition from fallback font to custom font occurs within milliseconds, often before the user even registers the swap.

You can also use the CSS unicode-range descriptor alongside subset files to ensure the browser only downloads the specific character sets requested by the page:

@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom-font-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153;
}

The Combined Result

Relying solely on font-display: swap prevents invisible text, but slow-loading full-weight fonts can create an unpleasant and prolonged FOUT. Relying solely on font subsetting makes fonts load faster, but network hiccups can still trigger the default multi-second invisible block.

By uniting both methods, font-display: swap guarantees that text is visible without delay, while font subsetting ensures the custom typography arrives so quickly that the swap is nearly imperceptible.