⚡IntermediatePerformance and Loading

Font Loading Optimization

Eliminate flash of invisible/unstyled text with font-display, preload, and self-hosted fonts.

Overview

Web fonts are one of the most common sources of layout shift and rendering delay. When a browser downloads a custom font, it may show invisible text (FOIT Flash of Invisible Text) or swap in the system font first then the web font (FOUT Flash of Unstyled Text). Optimization techniques: 1. font-display: swap shows fallback font immediately, swaps when custom font loads 2. Preload the font file fetches it earlier in the loading process 3. Self-host fonts avoids third-party DNS round trips 4. Use variable fonts one file covers all weights/styles 5. Subset fonts only include the characters your language needs 6. WOFF2 format ~30% smaller than WOFF

Key Points

  • •font-display: swap shows fallback immediately, then swaps minimizes FOIT
  • •font-display: optional only uses font if already cached best for subtle fonts
  • •Preloading the woff2 font file reduces the download latency significantly
  • •Self-hosting avoids third-party DNS resolution (faster than Google Fonts CDN for most users)
  • •System font stack (font-family: system-ui) zero network request, instant rendering
  • •Subsetting: only ship characters for the language (latin-ext vs all unicode)

Examples

Self-hosted font with font-display: swap

<!-- In <head>: preload the font before CSS discovers it -->
<link rel="preload" href="/fonts/inter-var.woff2"
      as="font" type="font/woff2" crossorigin>

<!-- In CSS or <style> -->
<style>
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-var.woff2') format('woff2');
  font-weight: 100 900;         /* Variable font range */
  font-display: swap;            /* Show fallback immediately */
  font-style: normal;
}

body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont,
               'Segoe UI', sans-serif;
}
</style>

Google Fonts with display=swap

<!-- Preconnect to Google Fonts origins -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

<!-- Load font with display=swap parameter -->
<link rel="stylesheet"
      href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap">

display=swap in the Google Fonts URL sets font-display:swap on the generated @font-face.

font-display values comparison

/* auto: browser default (usually block) */
font-display: auto;

/* block: invisible text for up to 3s, then swap */
font-display: block;

/* swap: show fallback immediately, swap when loaded */
font-display: swap;

/* fallback: 100ms block, then fallback; swap within 3s */
font-display: fallback;

/* optional: 100ms block, only use if cached */
font-display: optional;

swap is best for most text; optional is best for decorative/non-critical fonts.

Common Mistakes

  • Not adding crossorigin to font preload the preload is ignored silently
  • Using font-display: block for body fonts shows invisible text for up to 3 seconds
  • Preloading all font weights only preload the weight loaded immediately on the page

Note

The fastest font is a system font. Before adding a custom font, consider: is the visual difference worth 40-200KB of network overhead? Many high-performance sites use system fonts (GitHub, Wikipedia, Notion initially).

Exercises

Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.

Loading editor…

Completed: 0 / 5 exercises

Knowledge Check

Question 1 of 4

What does font-display: swap do?