Why fonts affect Core Web Vitals

Web fonts influence performance in two main ways. First, browsers typically delay text rendering until a web font has loaded, which can push back First Contentful Paint (FCP) and, in some cases, Largest Contentful Paint (LCP). Second, when a web font swaps in for its fallback and occupies a different amount of space on the page, the resulting layout shift contributes to Cumulative Layout Shift (CLS).

Loading fonts efficiently

Getting fonts to the browser as early as possible is the central goal of font loading optimization. This is especially important for third-party font hosts, which require separate connection setups. You can verify that fonts are requested in time by checking the Timing tab in the Chrome DevTools Network panel.

How @font-face actually triggers downloads

The @font-face declaration identifies the font name and the location of its file, but it does not by itself cause a download. A font is fetched only when styling that references it is applied to elements actually present on the page. For example, if your CSS declares a font family for h1 elements, the font will not load unless the document contains an h1.

This means stylesheets deserve as much attention as the font files. Their contents and delivery timing determine when the browser discovers which fonts are needed. Removing unused CSS and splitting stylesheets can therefore reduce the number of fonts a page downloads.

Inline font declarations for faster discovery

Moving font declarations and other critical styling into the <head> of the main document helps the browser find them sooner, without waiting for an external stylesheet to arrive. This technique works best when you can build tooling to inline only the CSS needed for initial rendering and deliver the rest in a non-render-blocking way.

Preconnect for third-party font origins

When fonts come from a third-party host, use the preconnect resource hint in the document head to open the connection early. Font files require a CORS connection, so if the stylesheet and the font files are served from different origins—as with Google Fonts—you need separate preconnect hints with the crossorigin attribute.

Preloading with caution

preload can make critical fonts discoverable very early, but it forces the browser to prioritize those resources at the expense of others. It also bypasses built-in content negotiation: preload ignores unicode-range declarations and should typically be used for a single font format. Inlining declarations and improving stylesheet delivery addresses the underlying late-discovery problem; reserve preload for when you rely on external stylesheets and need the most important fonts sooner.

Delivering font files

Faster delivery means faster text rendering, and delivering a font soon enough can also prevent the layout shifts that come from font swapping.

Self-hosting trade-offs

Self-hosting removes a third-party connection setup, but the real-world performance difference is not always clear-cut; the Web Almanac found that sites using third-party fonts sometimes rendered faster. To make self-hosting worth it, confirm you have a CDN and HTTP/2 in place. You must also replicate the optimizations that font providers typically handle automatically, such as subsetting and WOFF2 compression. CJK fonts in particular require careful optimization due to their large glyph counts.

WOFF2 should be your only format

WOFF2 has the best compression of modern font formats, using Brotli to produce files about 30% smaller than WOFF. With broad browser support, the recommendation now is to serve only WOFF2. If you need to support very old browsers, the simpler approach is to let those users see fallback fonts rather than serve additional formats.

Reduce file size with subsetting

Font files usually contain many glyphs, but pages rarely need all of them. Subsetting trims a font file down to the characters you use. The unicode-range descriptor in @font-face tells the browser which characters a font covers, and the file is loaded only when matching content appears on the page. This technique is frequently paired with subsetting to serve separate files for different scripts, such as Latin and Cyrillic.

The impact varies by script: Latin fonts typically contain on the order of 100 to 1,000 glyphs, while CJK fonts can exceed 10,000 characters. Some providers, like Google Fonts, generate subsets automatically. When self-hosting, you need to apply this optimization yourself, either through a provider API such as Google Fonts' text parameter or with tools like subfont and glyphhanger. Check font licenses to confirm subsetting and self-hosting are permitted.

Minimize the number of web fonts

The fastest font delivery is the one that never happens. System fonts and variable fonts are two ways to cut down on downloads.

System fonts are already installed on the user's device, so no network request is needed. They work well for body text; simply list system-ui as the font-family. Variable fonts combine many styles into one file, replacing several static font files. A variable font with a Weight axis, for example, can cover the range from light to extra bold.

Variable fonts are not automatically a win. Because they contain multiple styles, their file size is larger than a single static font. The sites that benefit most are those that genuinely use a variety of weights and styles.

Font rendering and layout stability

When a web font hasn’t loaded yet, the browser must decide whether to hold off on painting text until the font arrives, or to paint it immediately in a fallback font. Browsers differ in their default behavior: Chromium-based browsers and Firefox will block text rendering for up to 3 seconds while waiting for the font, while Safari will block it indefinitely.

The font-display attribute on an @font-face rule gives you control over this behavior. The choice you make has direct consequences for LCP, FCP, and CLS, so it’s worth choosing a strategy deliberately.

@font-face {
  font-family: Roboto, Sans-Serif
  src: url(/fonts/roboto.woff) format('woff'),
  font-display: swap;
}

There are five possible values for font-display, each defined by two phases:

Value Block period Swap period
Auto Varies by browser Varies by browser
Block 2-3 seconds Infinite
Swap 0ms Infinite
Fallback 100ms 3 seconds
Optional 100ms None
  • Block period — starts when the browser requests the web font. If the font isn’t ready, text is drawn with an invisible fallback, keeping it hidden from the user until the block period ends.
  • Swap period — follows the block period. If the web font loads during this window, it replaces the fallback font on the fly.

Choosing a strategy is a tradeoff between performance and visual fidelity, and the right call depends on your priorities:

  • If performance is the priority: use font-display: optional. The text renders without waiting any longer than 100ms, and there is no risk of a late font swap shifting the layout. The downside is that if the font arrives late, it isn’t used at all.
  • If you want fast text plus the web font: use font-display: swap but make sure the font is delivered early enough to avoid a layout shift when it is swapped in. A late arrival produces a noticeable visual jump.
  • If the web font must be used for the text: use font-display: block and deliver the font early to minimize the delay. Even though text is hidden initially, layout space is reserved using the fallback font metrics, and a shift may still occur once the web font loads and takes up different space. This shift can be less jarring than font-display: swap because the text itself isn’t seen to move.

These approaches can also be mixed on the same page, for example using font-display: swap for branding and other distinctive elements, and font-display: optional for body copy.

Icon fonts need special handling

The strategies that work well for regular text fonts often don’t hold up for icon fonts. The fallback glyphs for an icon font are typically very different in appearance, and may even represent different meanings entirely. That increases the chance of serious layout disruption. In many cases a fallback font simply isn’t practical.

Where possible, replace icon fonts with inline SVGs, which also improves accessibility. Modern versions of popular icon-font libraries generally support SVG output; Font Awesome’s SVG Sprites documentation and the Material Icons Guide cover the migration in detail.

Minimize the fallback-to-web-font shift

To reduce the CLS caused by swapping between fallback and web fonts, take advantage of the size-adjust attribute in your @font-face declarations, which lets you align the metrics of the two fonts more closely before the swap happens.

Web fonts remain a real performance bottleneck, but the growing set of tuning options makes it possible to reduce their impact substantially.