Font Loading Strategies
font-display, preload, subsetting, and metric overrides to balance FOIT/FOUT against CLS and LCP text.
- performance
- fonts
- cls
- font-display
- preload
Fonts sit on the critical path for text-heavy LCP and are a top CLS source when fallback metrics disagree with the webfont. Strategy is a product choice among invisible text, flash of unstyled text, and layout stability.
Docs: web.dev fonts, MDN font-display, CSS Font Loading.
font-display options
| Value | Behavior |
|---|---|
auto |
Browser default |
block |
Invisible text for a period (FOIT-ish) then swap |
swap |
Fallback immediately, swap when ready (FOUT; CLS risk) |
fallback |
Short block, then swap or stick |
optional |
Use webfont only if available quickly; great for repeat views |
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-latin.woff2') format('woff2');
font-weight: 100 900;
font-display: swap;
unicode-range: U+0000-00FF;
}
Preload critical faces only
<link
rel="preload"
href="/fonts/inter-latin.woff2"
as="font"
type="font/woff2"
crossorigin
/>
crossorigin is required for font preloads (anonymous CORS mode) even on same origin — missing it wastes the preload.
Preloading every weight/style competes with LCP images. One family, one critical weight is a common budget.
Subset and modern formats
- WOFF2 only for modern browsers.
- Subset to needed languages (
unicode-rangeor build-time subsets). - Variable fonts can replace many static files — still weigh file size.
Metric-matched fallbacks
@font-face {
font-family: 'InterFallback';
src: local('Arial');
size-adjust: 105%;
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
}
body {
font-family: Inter, InterFallback, system-ui, sans-serif;
}
Reduces CLS when using swap. See CLS tactics.
JS Font Loading API (optional)
await document.fonts.load('1em Inter');
document.documentElement.classList.add('fonts-ready');
Use for progressive enhancement, not to block rendering on all faces.
Self-host vs Google Fonts
Self-hosting avoids extra origins and CSP complexity; if using a font CDN, preconnect that origin. Privacy and GDPR discussions often push self-host.
Interview out-loud
“I preload one critical WOFF2 with crossorigin, subset unicode-range, pick font-display for the product tradeoff, and match fallback metrics to control CLS. Variable fonts and fewer weights keep bytes down.”
How this shows up in interviews
Be ready to define the metric or technique in one sentence, name one measurement approach (DevTools panel, web-vitals, or headers), and cite a concrete fix you would try first. Walk through a before/after: what the waterfall or flame chart showed, what you changed, and which percentile moved. Mention a tradeoff (complexity, caching correctness, or third-party business constraints) so the answer doesn’t sound like a blog checklist.
Production guardrails
Ship behind a flag when the change is risky, watch field p75 for the affected template for at least a few days, and keep a rollback path. Pair lab verification (throttled Performance/Network) with RUM so you don’t celebrate a Lighthouse-only win. Document the owner of any ongoing budget or third-party exception.
Related
Further depth
Teams often under-invest in this topic until an incident or CWV regression. Schedule a one-hour drill: reproduce the failure mode in DevTools, list the top three mitigations for your stack, and file tickets with owners. Revisit after the next major feature that touches networking, rendering, auth, or third parties — those are the moments regressions land. Keep primary documentation links in the runbook so on-call is not searching chat history at 2am.
Concrete artifacts to leave behind: a short architecture note, a CI assertion or header snapshot, and a dashboard panel (lab or field) that would have caught the last bug. Teaching the rest of the team the mental model matters as much as the one-line fix.
Further reading
Related guides
- CLS Optimization TacticsFix cumulative layout shift: dimensions, font metrics, reserved slots, and stable late-loading UI.
- Core Web VitalsLCP, INP, and CLS — what field data measures, good thresholds, and fixes that actually move the needle (per web.dev guidance).
- Avoiding Layout ThrashingStop forced sync layout loops: batch DOM reads and writes, use rAF, and fix janky measurement code.
- Caching Static Assets FingerprintingContent-hash filenames, long-cache headers, HTML revalidation, and CDN invalidation without stuck users.
- Code Splitting StrategiesRoute, component, and vendor splits: dynamic import(), magic comments, and avoiding over-splitting waterfalls.