core-web-vitalsperformanceseofont

font-display and Web Font Optimization: A Practical Guide to Better Core Web Vitals

How Web Fonts Affect Performance

Web fonts (Google Fonts, Adobe Fonts, etc.) improve design quality but can significantly hurt your Core Web Vitals scores if not configured properly.

The two main impacts are:

  • LCP (Largest Contentful Paint) degradation: Slow font loading blocks text rendering, delaying the largest content element
  • CLS (Cumulative Layout Shift) increase: Font swapping causes text to reflow when the web font replaces the fallback

Both problems can be largely eliminated with the correct font-display setting.

What Is font-display?

font-display is a CSS property that controls how the browser renders text while a web font is loading.

@font-face {
  font-family: 'Inter';
  src: url('/fonts/Inter-Regular.woff2') format('woff2');
  font-display: swap;
}

The Five font-display Values

ValueBehaviorLCP ImpactCLS Impact
autoBrowser default (usually same as block)BadLow
blockInvisible text until font loads (up to 3s)BadLow
swapShow fallback immediately, swap when loadedGoodSlightly higher
fallback100ms invisible, then fallback, short swap windowGoodLow
optional100ms invisible, use font only if already cachedBestLowest

Google's Recommendation

Google recommends font-display: swap. PageSpeed Insights and Lighthouse flag pages without it: "Ensure text remains visible during webfont load."

The key benefit of swap is that text is visible immediately, even before the web font loads. This dramatically improves LCP on text-heavy pages — which includes most blogs, documentation sites, and corporate websites.

The tradeoff is a potential minor CLS when the browser swaps from the fallback font to the web font. This can be mitigated with size-adjust (covered below).

Optimizing Google Fonts Loading

Step 1: Add display=swap

Simply append &display=swap to your Google Fonts URL:

<!-- Bad: no display parameter -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700" rel="stylesheet">

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

Step 2: Add preconnect Hints

Pre-establish connections to Google Fonts domains to reduce latency:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">

Step 3: Self-Host for Maximum Speed

Hosting font files on your own server eliminates external requests and gives you full cache control:

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

In Next.js, the next/font module automatically self-hosts fonts at build time with optimal font-display settings:

import { Inter } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  weight: ['400', '700'],
  display: 'swap',
});

Preventing CLS with size-adjust

When using font-display: swap, CLS occurs because the fallback font and web font have different metrics (character widths, heights, line spacing). The size-adjust property lets you tune the fallback to match:

@font-face {
  font-family: 'Adjusted Arial';
  src: local('Arial');
  size-adjust: 107%;
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
}

body {
  font-family: 'Inter', 'Adjusted Arial', sans-serif;
}

Calculating these values manually is tedious. Use these tools instead:

  • Fontaine — integrates with Next.js and Nuxt
  • next/font — automatically applies size-adjust for Google Fonts

Minimize Font Weights

Every font weight you load adds to the download size and delays LCP.

<!-- Bad: loading 9 weights you don't need -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">

<!-- Good: only the weights you actually use -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">

Rule of thumb: Most sites need only 2-3 weights (Regular + Bold, optionally Medium).

Preloading Critical Fonts

For fonts displayed above the fold, preloading tells the browser to start downloading immediately:

<link rel="preload" href="/fonts/Inter-Regular.woff2" as="font" type="font/woff2" crossorigin>

Warning: Only preload fonts you actually use in the initial viewport. Preloading unused fonts wastes bandwidth and can hurt performance.

Measuring the Impact

Verify your font optimizations with these tools:

  1. PageSpeed Insights — check LCP and CLS scores; confirm "Ensure text remains visible during webfont load" warning is resolved
  2. Chrome DevTools > Performance — visualize font loading in the timeline
  3. Chrome DevTools > Network — inspect font file sizes and load times
  4. IndexReady — enter any URL to get automated Core Web Vitals scoring and performance analysis

Frequently Asked Questions

Should I use font-display: swap or optional?

Use swap when brand consistency matters — it guarantees your web font will eventually display. Use optional when performance is the absolute priority — it may skip the web font entirely on slow connections, producing zero CLS. For most websites, swap is the right balance.

Does next/font handle font-display automatically?

Yes. next/font defaults to font-display: swap, self-hosts the font files at build time, and automatically calculates size-adjust values to minimize CLS. For Next.js projects, using next/font is the easiest and most effective approach.

Are CJK (Chinese/Japanese/Korean) fonts heavier than Latin fonts?

Significantly. CJK fonts contain thousands of glyphs, making their file sizes 10-20x larger than Latin fonts. Google Fonts serves CJK fonts in optimized subsets, but minimizing the number of weights is still critical. Loading a single Japanese font weight can exceed 1MB without subsetting.

Should I preload all my fonts?

No. Only preload fonts that render above the fold on initial page load. Preloading too many fonts competes for bandwidth with other critical resources like images and CSS, potentially making performance worse. Typically, preload one or two font files at most.

How do I check if font-display is working?

Open Chrome DevTools, go to the Network tab, and filter by "Font." Reload the page and observe: with swap, text should appear immediately in a system font, then switch to the web font once loaded. You can also throttle the network to "Slow 3G" to make the swap behavior more visible.

Ready to check your own site?

Enter a URL to get a free SEO & GEO readiness score instantly.

Score your site for free