TL;DR: Web fonts block rendering by default, causing Flash of Invisible Text (FOIT). This guide covers
font-display, font subsetting,preloadhints, and self-hosting to eliminate layout shifts and achieve fast first paint. Learn production patterns at tanstackship.com.
Introduction
Web fonts impact both visual design and performance. A slow font load can delay first contentful paint by several seconds. In 2026, with variable fonts and extensive typography options, font loading optimization is essential for Core Web Vitals scores.
Font Loading Strategy Comparison
| Strategy | FOIT Duration | FOUT Risk | Complexity | Best For |
|---|---|---|---|---|
font-display: swap | 0ms | High | Low | Body text |
font-display: optional | 100ms | Medium | Low | UI icons |
font-display: block | 3s | None | Low | Brand headings |
| Preload + swap | 0ms | Medium | Medium | Hero text fonts |
| Critical font inlining | 0ms | Low | High | Above-fold text |
| Self-hosting (WOFF2) | Varies | Low | Medium | All production use |
Implementing font-display
/* Optimal font-display for body text */
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-var.woff2') format('woff2');
font-display: swap;
font-weight: 100 900;
unicode-range: U+0000-00FF;
}
The swap value gives the font a 100ms block period, then swaps to a fallback if the font hasn't loaded. This eliminates invisible text while avoiding layout shifts.
Preloading Critical Fonts
<!-- Preload your primary font -->
<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>
<!-- Preload only the weight you need for above-fold content -->
<link rel="preload" href="/fonts/inter-semibold.woff2" as="font" type="font/woff2" crossorigin>
Preloading moves font discovery to the initial HTML parsing phase, before the browser encounters the CSS @font-face declaration.
Font Subsetting for Size Reduction
| Technique | Size Reduction | Implementation |
|---|---|---|
| Basic Latin subset | 60-70% | pyftsubset or glyphhanger |
| Variable font subset | 50-60% | Combined axis + character subset |
| Unicode-range splitting | 40-50% | CSS unicode-range per @font-face |
| WOFF2 compression | 30-50% | Automatic with modern tools |
# Subset a font to basic Latin with glyphhanger
npx glyphhanger https://your-site.com --subset=*.ttf --formats=woff2
# Result: Font file reduced from 150KB to 45KB
Self-Hosting vs Google Fonts
// Self-hosted font loading in TanStack Start
import { defineFont } from '~/lib/fonts'
export const primaryFont = defineFont({
src: '/fonts/inter-var.woff2',
display: 'swap',
preload: true,
fallback: {
fontFamily: 'system-ui',
sizeAdjust: '105%' // Match metrics to reduce layout shift
}
})
Self-hosting eliminates the DNS lookup, TCP connection, and TLS negotiation to Google's servers, saving 200-400ms on first load.
Conclusion
Font loading optimization directly impacts Largest Contentful Paint and Cumulative Layout Shift. Prioritize self-hosting WOFF2 fonts, use font-display: swap for body text, preload critical fonts, and subset aggressively. These techniques ensure your typography enhances rather than hinders user experience.
For the full picture, see our Preload, Prefetch, and Preconnect Guide for resource hint best practices, and Critical CSS and Inlining Strategies for CSS delivery optimization. Combine with the Core Web Vitals Optimization Guide and CDN Selection and Configuration Guide for a complete performance strategy.