PerformanceMay 17, 20269 min read

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.

StoreVitals Team

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: swap or fallback. 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: block with 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-range in 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

  1. Single variable font file per family (not separate weight files)
  2. Total font bytes < 150KB on landing/PDP pages
  3. Critical font (LCP-element font) preloaded with rel="preload" + crossorigin + type
  4. font-display: swap or fallback for body text; optional if CLS is > 0.1
  5. Self-hosted (not loaded from fonts.googleapis.com)
  6. Latin subset only (or appropriate subset for target geography)
  7. woff2 format (woff and ttf are obsolete; eot is for IE only)
  8. Cache-Control headers set to 1 year for font files (cache-control: public, max-age=31536000, immutable)
  9. No FOIT longer than 100ms (test with throttled connection in DevTools)
  10. 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.

font loadingCore Web VitalsLCPCLSperformancevariable fonts

See these issues on your store?

Run a free scan and find out in seconds.

Run Free Scan