Font Loading Strategy for Ecommerce: Variable Fonts, Preload, and the FOIT/FOUT Tradeoffs
Web fonts account for 100-400KB on most ecommerce sites and are the third-largest contributor to LCP after hero images and JavaScript. The 6-point font loading audit: variable fonts, font-display, preload, subset strategy, and when FOIT actually beats FOUT.
Web fonts are one of the most-overlooked performance levers in ecommerce. A typical store loads 3-5 font files totaling 100-400KB, and on slow mobile connections these files compete for bandwidth with the hero image — the LCP element — and the critical CSS. The result: stores routinely lose 100-300ms of LCP and add 0.05-0.15 to their CLS score because of font handling mistakes that are entirely fixable.
The pattern below is what works in production for Shopify, WooCommerce, and custom-platform stores. Six audit points, ordered from highest-leverage to detail-polish.
1. Variable Fonts vs Multi-File Loading
Traditional font loading: one file per weight per style. A site using regular, regular-italic, bold, and bold-italic loads 4 font files. Multiply by serif and sans-serif font families and you're at 8 files.
Variable fonts pack all weights and styles into a single file. A variable Inter font file is ~150KB and covers every weight from 100 to 900 plus italics. The same coverage with traditional fonts is 8 files totaling 250-400KB.
Adoption check: Google Fonts, Adobe Fonts, and most modern font foundries offer variable versions. The CSS syntax:
@font-face {
font-family: "Inter";
src: url("/fonts/inter-variable.woff2") format("woff2-variations");
font-weight: 100 900;
font-style: normal;
}
For stores still loading 4-8 separate font files, switching to a variable font cuts total font bytes by 40-60% and reduces HTTP request count by the same factor. This is usually a one-afternoon migration with measurable LCP improvement.
2. font-display: swap vs optional vs fallback
The font-display CSS property controls what happens while a font is loading. Four values matter:
- auto / block: hides text until font loads (FOIT — Flash of Invisible Text). On slow connections, customers see blank space for 1-3 seconds. Worst for perceived performance and CLS-neutral only because nothing renders.
- swap: shows text in fallback font immediately, swaps to web font when loaded (FOUT — Flash of Unstyled Text). Best for LCP but causes CLS as text resizes.
- fallback: 100ms block period, then 3-second swap period, then fallback if not loaded. Compromise — usually the right default.
- optional: 100ms block period, then commit to fallback. Best for CLS — eliminates layout shift from font swapping entirely. Slight LCP penalty.
The right choice depends on whether your font is critical to brand identity:
- For body text:
font-display: swaporfallback. Fast loading matters more than exact font. - For brand/logo text: this should be SVG, not text. If it must be text, use
font-display: blockwith preload. - For high-CLS sensitivity:
font-display: optional. Accepts that some customers will never see your web font on first visit but eliminates layout shift.
3. Preloading Critical Fonts
Adding a <link rel="preload"> hint for critical fonts tells the browser to start downloading the font before parsing CSS. For LCP-relevant fonts (heading font on a hero, body font on landing pages), this saves 100-300ms.
<link rel="preload" href="/fonts/inter-variable.woff2"
as="font" type="font/woff2" crossorigin>
Three rules:
- crossorigin attribute is required even for same-origin fonts. Without it, the preload doesn't match the actual font request and the browser downloads the font twice.
- Only preload the 1-2 most critical fonts — preloading everything defeats the purpose. The font used in the LCP element (usually hero heading) is the only one that actually moves LCP.
- type="font/woff2" matters — without it, the preload may not be applied correctly in some browsers.
4. Self-Hosting vs Google Fonts CDN
The conventional wisdom shifted in 2022 when Chrome stopped sharing the font cache across domains. Before: a font loaded from Google Fonts CDN on Site A might be cached when you visited Site B that used the same font. After: every site's fonts are downloaded fresh.
Implications:
- The cross-domain DNS lookup and TLS handshake to fonts.googleapis.com costs 50-200ms — pure overhead with no cache benefit
- Self-hosting fonts removes that overhead entirely
- Self-hosting also removes the third-party privacy concerns (Germany's 2022 ruling that Google Fonts CDN violates GDPR forced many EU stores to self-host)
- Modern font services (Bunny Fonts, Fontsource) make self-hosting trivial
Recommendation: self-host. The performance gain is small but real, and the privacy/compliance gain is significant for EU traffic.
5. Subset and Latin-Only Strategy
Most font files include glyphs for many languages — Cyrillic, Greek, Vietnamese, extended Latin. For a US-targeting ecommerce store, 50-70% of the font file is unused glyphs.
Subsetting strips unused glyphs from the font file. Google Fonts subsets automatically when you use their CDN — you specify subset=latin in the URL. For self-hosted fonts:
- Use a tool like subfont or EverythingFonts to generate a subset
- For international stores, generate multiple subsets and use
unicode-rangein CSS to load only the subset needed for the visitor's language - Typical subset reduction: a 150KB variable Inter font becomes 50-70KB after Latin-only subsetting
6. The Font Loading Audit Checklist
- Single variable font file per family (not separate weight files)
- Total font bytes < 150KB on landing/PDP pages
- Critical font (LCP-element font) preloaded with
rel="preload"+ crossorigin + type - font-display: swap or fallback for body text; optional if CLS is > 0.1
- Self-hosted (not loaded from fonts.googleapis.com)
- Latin subset only (or appropriate subset for target geography)
- woff2 format (woff and ttf are obsolete; eot is for IE only)
- Cache-Control headers set to 1 year for font files (
cache-control: public, max-age=31536000, immutable) - No FOIT longer than 100ms (test with throttled connection in DevTools)
- CLS contribution from fonts < 0.05 (test via Chrome's Performance panel)
The font layer is often the lowest-hanging performance fruit on an ecommerce site — 30-60 minutes of work to migrate to a variable font, add preload, and self-host typically yields a 100-300ms LCP improvement and a measurable CLS reduction. StoreVitals scans surface font byte totals, third-party CDN usage, missing preload hints, and font-display settings across the site. Audit it once; the optimizations stick.