Load custom fonts without flash of unstyled text (FOUT) or layout shift — a practical guide.
The Font Loading Problem
Custom fonts cause two visible issues: flash of invisible text (FOIT) where nothing shows until the font loads, and flash of unstyled text (FOUT) where the fallback font is replaced — causing a layout shift.
The Best Approach: font-display: swap + Size-Adjust
Use font-display: swap so text is visible immediately with a fallback font. Then use size-adjust on the fallback to match the custom font's metrics. This eliminates the layout shift when fonts swap.
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap;
}
@font-face { font-family: 'Inter Fallback'; src: local('Arial'); size-adjust: 107%; ascent-override: 90%; descent-override: 22%; line-gap-override: 0%; }
Self-Host Your Fonts
Google Fonts is convenient but adds a DNS lookup and connection to an external server. Self-hosting eliminates that latency. Download the .woff2 files and serve them from your own domain.
Preload Critical Fonts
Tell the browser to start downloading fonts early:
Only preload the weights you use above the fold. Preloading every weight defeats the purpose.
Subset Your Fonts
If your site is in English, you do not need Cyrillic or CJK glyphs. Subsetting reduces font file size dramatically — sometimes from 200KB to 20KB.
Tools like glyphhanger or pyftsubset can generate subsets automatically.
Variable Fonts
One variable font file replaces multiple weight files. A single 40KB variable font can cover weights 100-900 and italic variants. The browser interpolates smoothly.
Next.js Integration
next/font handles most of this automatically — self-hosting, subsetting, and generating the CSS. If you use Next.js, start there before implementing manual font loading.