Web Fonts and Core Web Vitals: How to Load Typefaces Without Slowing Your Site
Table of contents
Loading web fonts without slowing the page comes down to a decision almost nobody reviews: when the typeface hasn't arrived, the browser chooses between painting the text with another font or leaving it invisible. That default choice moves LCP, FCP and CLS, and it takes a few lines of CSS to fix.
Why a Typeface Can Cost You LCP and CLS
What Happens Between Your CSS and the First Glyph
Start by clearing up a misunderstanding: an @font-face downloads nothing. The file is requested when some style actually uses the font, so if it lives in an external stylesheet, the browser does not know the typeface exists until it has that CSS.
The lifecycle has three stages that run through this article: discovery, delivery and rendering. The two problems blamed on fonts are born in the third, but they are fixed in the first two.
Invisible Text and Layout Shift: Two Different Failures
FOIT (flash of invisible text) is text you cannot see. According to web.dev, Chromium and Firefox block text rendering for up to 3 seconds if the font has not arrived, and Safari blocks it indefinitely. When that text block is the largest element on screen, it hits LCP directly.
FOUT (flash of unstyled text) is the opposite: the text paints with the system font and is then swapped. It appears sooner, but the swap can move the layout. Here is the nuance almost nobody mentions, stated by the Lighthouse audit itself: FOIT and FOUT have the same impact on CLS when the custom font replaces the temporary system font. Removing invisible text does not remove the shift.
How Big the Problem Is, With Data
The Web Almanac 2025 found that 87% of the mobile pages analysed load at least one web font, and that for font resources only 24% declare dns-prefetch, 22% use preconnect, and 15% on desktop and 16% on mobile use preload. A minority optimizes what the majority uses.
font-display: The Five Values and Which One Is Yours
The Two Periods That Explain Everything
Every font-display value is explained by two periods. During the block period, if the font is missing, the text is drawn with the fallback but invisible. During the swap period, the fallback is visible and, if the custom font arrives, it is substituted.
| Value | Block period | Swap period |
|---|---|---|
auto | Browser dependent | Browser dependent |
block | 2-3 seconds | Infinite |
swap | 0 ms | Infinite |
fallback | 100 ms | 3 seconds |
optional | 100 ms | No swap |
The property is Baseline since January 2020, according to MDN: compatibility is no excuse for leaving it undeclared.
Choose by Priority, Not by Habit
If performance comes first, use optional: text never waits more than 100 ms and there is no swap shift, at the cost of not using the font on that visit if it arrives late. If you want text fast and still want the brand typeface most of the time, use swap, accepting the shift risk on the first load. If the text must be rendered in the font no matter what, use block, and then delivery has to be solid.
Icon Fonts: The Case Where swap Fails
An icon font breaks the previous logic. The fallback does not show another typeface, it shows another character with another meaning: a phone icon becomes an arbitrary letter. The way out is SVG, which is also better for accessibility; the detail is in the article on website accessibility.
Making the Font Arrive Sooner: Discovery and Delivery
Declare @font-face in the head, Not at the End of an External Sheet
web.dev recommends inlining font declarations, along with critical CSS, in the <head>: the browser discovers the file sooner. And it warns about the opposite: do not inline font files, because they bloat the main document and delay everything else.
preconnect and preload, and the Two Classic Traps
For Google fonts, preconnect to both origins: the CSS one and the file one, the latter with crossorigin because fonts travel over CORS.
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
Two expensive traps. First: preload ignores unicode-range, so preloading one file preloads the whole range; a single format is enough. Second: a preload competes for bandwidth and is only justified for the font that paints first. The full family of hints is in the resource hints guide.
WOFF2, Subsets and unicode-range
WOFF2 uses Brotli and compresses about 30% better than WOFF, so if you serve one format, make it that one. Then size: a Latin font runs between 100 and 1,000 glyphs, while a CJK font can exceed 10,000. unicode-range tells the browser which characters each file covers and makes it download only the one it needs.
Google Fonts does this by default, splitting the family into several @font-face blocks, and when you move to self-hosting that optimization is lost unless someone replicates it. In the Google Fonts API, subset= is ignored when the browser supports unicode-range.
Fewer Files: Variable Fonts and system-ui
A variable font replaces several weight and style files with one: fewer requests. The honest trade-off is that it weighs more than a single-style file, so it pays off for whoever uses many weights. For body copy there is always font-family: system-ui, which costs zero downloads.
Self-Hosting vs Google Fonts
A nuance that breaks the dogma: the Web Almanac found that sites using third-party fonts rendered faster than those serving their own. If you self-host, make sure you have a CDN and HTTP/2 and apply subsetting and WOFF2 the way a provider would; the way to decide without faith is to serve one file from your own domain and compare transfer time including the connection.
Killing the Shift: Fallback Font Metrics
size-adjust: What It Actually Scales
size-adjust is a multiplier for outlines and metrics: it scales glyph advances and baseline tables, which is exactly what you need to align two fonts at the same font-size. It is Baseline since September 2023 according to MDN, so the technique is no longer experimental.
@font-face {
font-family: "Adjusted Fallback";
src: local("Arial");
size-adjust: 107%;
}
The other overrides (ascent-override, descent-override, line-gap-override) fine-tune line height and are explained in the article on CLS and layout shifts.
Finding the Percentage Without Guessing
Load both fonts on a test page, overlay a paragraph of real text in the language you serve (not "Lorem ipsum": it has a different width distribution) and adjust until line width and first-line position match. Check it in DevTools, Rendering panel, with Layout Shift Regions enabled, and measure on mobile and desktop with the site's real font-size.
How to Check It in 2026: Lighthouse 13, PageSpeed Insights and Search Console
The Audit Is Now Called font-display-insight
Lighthouse 13, published in October 2025, consolidated performance audits into insights and removed the old ones from both the report and the JSON. The classic text-visibility audit is now font-display-insight. It flags font URLs that may cause invisible text, but it does not decide whether your font-display choice is right for your brand.
From Lab to Field
The lab is a simulated load; the field is real users, and the difference is explained in PageSpeed Insights: field and lab data. A change to font-display shows up in the lab instantly and in the field once the 28-day window of the Core Web Vitals report fills up; the thresholds are in what Core Web Vitals are. And the honest frame: fonts are not a ranking factor; they move LCP, FCP and CLS, and Google says it uses Core Web Vitals in its ranking systems, with no single page experience signal (stated on its page experience page).
Checklist and Repeated Mistakes
- One typeface family if the design allows it: every extra family is a download.
- WOFF2 as the only format served.
@font-facein the<head>, never at the end of an external sheet.font-display: swapfor headlines and brand,optionalfor body.- At most one preloaded font, and with
crossorigin. - Subsets through
unicode-rangeif the family is served from your domain. - Fallback with a measured
size-adjust, not one copied from a blog. - Icons as SVG, not as an icon font.
The font is not a ranking factor: it is a rendering decision you pay for in LCP and CLS. Choose the strategy once, measure it with the same method before and after, and forget about it until the next Core Web Vitals review.