πŸ”  CSS font-display β€” Complete Tutorial

The font-display descriptor controls **how web fonts load and render** on your website. It defines what the browser should do **while the custom font is loading**, and how it should behave if the font takes too long to load or fails entirely.

>>β€œfont-display lets you balance performance and design β€” swap instantly, wait for font, or fall back gracefully.”

Note

βœ” Only works inside @font-face
βœ” Crucial for performance and avoiding invisible text (FOIT)
βœ” Affects how quickly text appears to users

πŸ“¦ Syntax

@font-face with font-display

@font-face {
  font-family: "MyFont";
  src: url("myfont.woff2") format("woff2");
  font-display: swap;
}
ValueBehavior
autoBrowser decides
blockText invisible briefly, then custom font appears
swapText shows immediately using fallback, then swaps to custom
fallbackShows fallback instantly, swaps only if font loads quickly
optionalFallback shown; custom font used only if cache/quick

🎨 Understanding font-display Modes

1️⃣ font-display: auto

Default behavior. Browser chooses strategy depending on connection, font size, etc.

2️⃣ font-display: block (FOIT β€” Invisible Text)

block

@font-face {
  font-family: "MyFont";
  src: url("font.woff2");
  font-display: block;
}

The browser hides text until the font loads (up to 3 seconds). After that, text appears in custom font or fallback if failed.

Note

❌ Causes FOIT β†’ Flash of Invisible Text

3️⃣ font-display: swap (Recommended)

swap

@font-face {
  font-family: "MyFont";
  src: url("font.woff2");
  font-display: swap;
}

Text appears immediately using fallback font β†’ once custom font loads, the text β€œswaps” to the custom font.

Note

βœ” Best for performance + usability

4️⃣ font-display: fallback

fallback

@font-face {
  font-family: "MyFont";
  src: url("font.woff2");
  font-display: fallback;
}

Text uses fallback font first. Custom font replaces it only if loaded within ~100ms.

5️⃣ font-display: optional (Best for very fast sites)

optional

@font-face {
  font-family: "MyFont";
  src: url("font.woff2");
  font-display: optional;
}

If the font loads instantly (from cache), it appears. Otherwise, the browser **sticks to the fallback font permanently**.

Note

βœ” Best for users on slow networks βœ” Used by Google Fonts for speed

🎯 Visual Comparison

ModeInitial TextAfter Font Loads
block❌ Invisibleβœ” Custom
swapβœ” Fallback visibleβœ” Switches to custom
fallbackβœ” Fallback visible⚠️ Swaps only if fast
optionalβœ” Fallback visible⚠️ Usually remains fallback

πŸ§ͺ Real-World Use Cases

1️⃣ Best for UI Websites β†’ swap

UI font loading

@font-face {
  font-family: "Inter";
  src: url("Inter.woff2");
  font-display: swap;  /* best UI experience */
}

2️⃣ Article / Blog Typography β†’ fallback

blog

@font-face {
  font-family: "Merriweather";
  src: url("Merriweather.woff2");
  font-display: fallback;
}

3️⃣ Performance-First Apps β†’ optional

performance

@font-face {
  font-family: "Poppins";
  src: url("poppins.woff2");
  font-display: optional;
}

Prevents layout shift on slow networks.

⚠️ Common Mistakes

  • ❌ Using block on text-heavy websites (causes invisible text)
  • ❌ Assuming font-display works outside @font-face
  • ❌ Not providing fallback fonts (system-ui, serif, sans-serif)
  • ❌ Using large unoptimized fonts (slows down swap)

Note

Always include a performance-friendly fallback font stack.

πŸ”₯ Summary

font-display determines how your text behaves while custom fonts load. Choosing the right mode is crucial for UX, speed, and visual stability.

  • swap β†’ Best balance (recommended)
  • fallback β†’ Good for reading content
  • optional β†’ Top performance option
  • block β†’ Avoid unless necessary
>>β€œFast text beats fancy text β€” font-display ensures both can coexist.”