Without font-display, the browser's default behavior is to block text rendering for up to 3 seconds while waiting for the web font to download — Flash of Invisible Text (FOIT). On slow connections, users see blank text boxes where your content should be (ISO-25010 time-behaviour). Even on fast connections, FOIT contributes to CLS when the font finally loads and changes character widths, causing visible layout shift. Every web font without font-display: swap is a potential 3-second invisible-text window.
Medium because missing `font-display` causes up to 3 seconds of invisible text on slow connections and triggers CLS-contributing font swaps when the web font finally loads.
Add font-display: swap to every @font-face declaration so the browser shows fallback text immediately and swaps in the web font when ready:
/* src/styles/fonts.css */
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom-regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
With next/font/google, font-display: swap is applied automatically:
// src/app/layout.tsx
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] }) // font-display: swap included
With Google Fonts via <link>, append &display=swap to the URL:
<link href="https://fonts.googleapis.com/css2?family=Inter&display=swap" rel="stylesheet">
Set a size-similar fallback font stack (e.g., Arial for Inter) to minimize the visual jump on swap.
ID: performance-core.image-media-optimization.font-display
Severity: medium
What to look for: Count all relevant instances and enumerate each. Check @font-face declarations for font-display property. Look in CSS files, font loading libraries (next/font, Google Fonts, Typekit), or <link> tags for web fonts. Verify all font faces have font-display set to swap, optional, or other non-blocking value.
Pass criteria: All @font-face declarations include font-display: swap (or another non-blocking value). At least 1 implementation must be verified. Fonts are loaded asynchronously, and fallback fonts display immediately.
Fail criteria: font-display is missing (defaults to auto, blocking on some browsers), or set to block (FOIT). Fonts block text rendering for 3+ seconds.
Skip (N/A) when: The project uses only system fonts or no web fonts.
Detail on fail: Specify the font loading strategy. Example: "Five @font-face rules with no font-display; text blocks for 2.5s while font loads (FOIT). No fallback font specified" or "font-display: block on all fonts; poor UX on slow networks".
Remediation: font-display: swap shows fallback text immediately and swaps in the web font when ready:
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2');
font-display: swap; /* show fallback immediately, swap in web font */
}
With next/font:
import { Inter } from 'next/font/google'
const inter = Inter() // already optimized with font-display: swap
With Google Fonts link:
<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&display=swap" rel="stylesheet">