Fix Ensure Text Remains Visible During Webfont Load in WordPress

Quick Summary
- Add font-display: swap to every @font-face rule, append display=swap to Google Fonts URLs, and set "fontDisplay": "swap" in a block theme's theme.json.
- Since Lighthouse 13 the warning is the Font display insight, and it only passes swap or optional; fallback, WordPress's default for theme.json fonts, fails.
- A style_loader_src filter can add display=swap to Google Fonts that a theme or plugin enqueues; clear page and CSS caches, then test again.
To fix Ensure text remains visible during webfont load in WordPress, add font-display: swap to every @font-face rule your site loads. For Google Fonts, add &display=swap to the stylesheet URL. For fonts a block theme defines in theme.json, add "fontDisplay": "swap". Since Lighthouse 13, which PageSpeed Insights has used since October 2025, the warning appears as the Font display insight, and it only passes swap and optional. fallback, which WordPress uses by default for the fonts it prints itself, gets flagged too.
What does the warning mean?
While a web font downloads, the browser has to decide what to do with the text that uses it. The font-display descriptor tells it. The CSS Fonts specification defines the values like this:
block: hide the text for a short time, 3 seconds recommended, then show a fallback font until the web font arrives.auto, the default, lets the browser decide, and many browsers behave likeblock.swap: hide the text for 100ms or less, show a fallback font, and switch to the web font whenever it arrives.fallback: likeswap, but if the font takes longer than about 3 seconds, keep the fallback font.optional: use the web font only if it’s ready almost immediately; otherwise use the fallback for the whole page view.
Hidden text while a font loads is called a flash of invisible text (FOIT). Visitors see empty space where your headings and paragraphs should be.
To see what the current insight accepts, the same page was tested with Lighthouse 13.5 four times, with a font file that took 800ms to download. With no font-display and with fallback, the Font display insight failed with “Est savings of 810 ms”. With swap and with optional, it passed. That matches Chrome’s documentation for the insight: set font-display to swap or optional to pass.
How do you fix it for fonts you host yourself?
Add font-display: swap to each @font-face rule:
@font-face {
font-family: 'Pacifico';
font-style: normal;
font-weight: 400;
src: url('fonts/pacifico-regular.woff2') format('woff2');
font-display: swap;
}font-familyis the name you use for the font in the rest of your CSS, here'Pacifico'.srcpoints to the WOFF2 file. A relative URL is relative to the stylesheet, so this looks for afontsfolder next to it. Adding custom fonts with @font-face covers the rule in more detail.font-display: swapis the line the warning is about. Every@font-faceneeds it, one per weight and style.
Put the rule in the stylesheet that declares your fonts, usually your child theme’s style.css. An earlier version of this example pointed src at a file on fonts.gstatic.com with a version number in its path. Google changes those paths when it updates a font, and its stylesheet now serves version 23 of Pacifico, so don’t copy them into your own CSS: host the file yourself, or load Google’s stylesheet as shown next.
How do you fix it for Google Fonts?
Add display=swap to the stylesheet URL. Google then adds font-display: swap to every @font-face rule it returns; without the parameter, its rules have no font-display at all. In WordPress, enqueue the stylesheet from your child theme’s functions.php:
function my_enqueue_google_fonts() {
wp_enqueue_style(
'my-google-fonts',
'https://fonts.googleapis.com/css2?family=Pacifico&family=Roboto:wght@400;700&display=swap',
array(),
null
);
}
add_action( 'wp_enqueue_scripts', 'my_enqueue_google_fonts' );The CSS2 API takes one family= parameter per font. The null version stops WordPress from adding ?ver= to Google’s URL. In WordPress 7.1.2, the printed link kept both families and display=swap.
What if a theme or plugin loads the fonts?
If the Google Fonts link comes from your theme or a plugin, you can add the parameter as the URL is printed, with the style_loader_src filter:
function my_google_fonts_swap( $src ) {
if ( str_contains( $src, 'fonts.googleapis.com' ) && ! str_contains( $src, 'display=' ) ) {
$src .= ( str_contains( $src, '?' ) ? '&' : '?' ) . 'display=swap';
}
return $src;
}
add_filter( 'style_loader_src', 'my_google_fonts_swap' );- It only changes Google Fonts URLs that don’t already have a
displayparameter, and leaves every other stylesheet alone. - It appends the parameter as text instead of using
add_query_arg(). On a URL with twofamily=parameters,add_query_arg()kept only the last one, so a font disappeared. - It only reaches stylesheets loaded with
wp_enqueue_style(). A<link>tag hard-coded in a template or an@importinside a CSS file needs editing by hand, or a setting in the theme or plugin.
How do you fix it for theme.json fonts in a block theme?
Block themes such as Twenty Twenty-Five define their fonts in theme.json, and WordPress prints the @font-face rules itself. When a font face doesn’t set fontDisplay, WordPress uses fallback, which the insight flags. Twenty Twenty-Five’s font faces don’t set it. Add it to each entry in fontFace:
{
"fontFamily": "Manrope",
"fontStyle": "normal",
"fontWeight": "200 800",
"fontDisplay": "swap",
"src": [ "file:./assets/fonts/manrope/Manrope-VariableFont_wght.woff2" ]
}In a test with WordPress 7.1.2, the same font face printed font-display:fallback without the fontDisplay line and font-display:swap with it.
Make the change in a child theme’s theme.json, and copy the parent’s whole fontFamilies list into it: a child theme’s list replaces the parent’s instead of merging with it. In the test, a child list with only Manrope removed the parent’s other font. The Google Fonts collection in the Site Editor’s font library doesn’t set fontDisplay either, so fonts installed from there also get fallback.
Does swap have a downside?
The text can shift when the web font replaces the fallback, if the two fonts have different proportions, and that counts toward Cumulative Layout Shift. Choose a fallback in your font-family stack with similar proportions, and if the shift is still visible, font-size-adjust or the size-adjust descriptor can bring the fallback closer. optional never swaps fonts after the first paint, so it avoids the shift, at the price of showing the fallback font to visitors whose font hasn’t loaded in time.
Preloading the font used by the first screen gets it there sooner, but it doesn’t clear the warning on its own, since the insight checks the font-display value. On this site, exactly one font file is preloaded and every @font-face uses swap; designing pages that feel fast before they load shows the preload tag and why it needs the crossorigin attribute.
After any of these changes, clear your page cache and any CSS optimization cache, then run the test again.
If your site has more Core Web Vitals warnings like this one, see my WordPress SEO and performance services.
Related articles
- adding custom fonts with CSS @font-face: the rule this fix changes
- deferring JavaScript without a plugin: another common PageSpeed fix in functions.php
- fixing the browser caching warning: long cache times for images, CSS and JavaScript
