Type something to search...
Variable Fonts vs. Static Fonts: A Performance Comparison

Variable Fonts vs. Static Fonts: A Performance Comparison

A team migrates its marketing site from four static weights to a single variable font, expecting a performance win, and the font payload goes up. Another team does the same migration and cuts font bytes by 40% while removing a visible double swap from every page load. Both results are real. Whether a variable font is faster than static fonts is not a yes-or-no question; it depends on how many weights you use, which axes the file includes, how it is subset, and how the files are loaded.

This article compares variable and static fonts on the metrics that actually affect page speed: bytes, requests, swap behavior and layout shift, caching, and rendering cost. It also gives you a repeatable benchmark so you can measure your own font instead of relying on rules of thumb. For the concepts behind variable fonts, see what variable fonts are and why you should use them.

What Exactly Are We Comparing?

A fair comparison holds everything else equal:

  • Same typeface, in both its static and variable forms.
  • Same character set, because subsetting has a far bigger effect on size than the variable or static choice.
  • Same format, WOFF2, since it is the right format for every modern browser. (If you are unsure why, see WOFF2 vs. WOFF vs. TTF.)
  • Same weights and styles in use on the page.

Comparing a full, unsubset variable TTF against a Latin-subset static WOFF2 tells you nothing about variable fonts. It only tells you that compression and subsetting work.

File Size: Where the Break-Even Point Is

A variable font stores each glyph outline once, plus deltas describing how the points move along each axis. A static font stores one complete set of outlines per weight. That leads to a predictable pattern:

  • One variable file is larger than one static weight, because it carries the delta data.
  • One variable file is usually smaller than several static weights combined, because outlines are not duplicated.
  • Every extra axis (width, optical size, grade) adds more delta data.

The table below shows a representative pattern for a Latin-subset sans-serif family in WOFF2. The exact numbers vary by typeface, glyph count, and design complexity, so treat them as orders of magnitude and measure your own font with the script later in this article.

SetupFilesTypical total size
1 static weight115–30 KB
2 static weights (400, 700)230–60 KB
4 static weights (400, 500, 600, 700)460–120 KB
Variable, weight axis only130–70 KB
Variable, weight + optical size150–110 KB
Variable, weight + width + grade + opsz190–200 KB

From that pattern, the rules of thumb are:

  • One weight: static wins on bytes.
  • Two weights: roughly a tie; static is often slightly smaller.
  • Three or more weights: a weight-only variable font almost always wins.
  • Italics: usually a second file in both approaches, so the comparison holds separately for upright and italic.
  • Extra axes you do not use: pure waste; they can make a variable font lose even at four weights.

Instancing narrows the gap

You do not have to choose between a full variable font and individual static files. fontTools can cut a variable font down to just the range you use, which is called partial instancing:

fonttools varLib.instancer Brand-Variable.ttf wght=400:700 opsz=drop -o Brand-400-700.ttf

wght=400:700 keeps only that slice of the weight axis, and opsz=drop pins optical size at its default and removes it. A 400–700 range carries less delta data than 100–900, so the file shrinks, sometimes significantly. Combine this with subsetting, covered in what font subsetting is and how it reduces file size, and a variable font can be competitive even at two weights.

Requests and Connection Overhead

On HTTP/1.1, every extra file meant another request competing for one of six connections. On HTTP/2 and HTTP/3, requests are multiplexed over a single connection, so the per-request cost is much smaller. That does not make request count irrelevant:

  • Each request has header and scheduling overhead, and fonts are fetched at high priority, so several of them compete with your LCP image and critical CSS.
  • Each file is a separate preload decision. With static fonts you either preload four files and crowd the network, or preload one and let the others arrive late.
  • Discovery is per face. The browser requests a static weight only when it finds an element that uses it, which may be after layout. A variable font is requested once, as soon as any weight is needed.

In practice, a variable font's single request is a modest but real advantage once you use three or more weights.

Swap Behavior and Layout Shift

This is where variable fonts often win even when bytes are roughly equal.

With static fonts, regular text, bold text, and headings each depend on a different file. They arrive at different times, so the page goes through several partial swaps:

  1. Body text swaps from fallback to the regular weight.
  2. Bold phrases swap a few hundred milliseconds later.
  3. Headings in a semibold or heavy weight swap last.

Each swap can reflow text and contributes its own layout shift. With a variable font, all weights swap in a single frame, so you get at most one reflow. For background on how this feeds into CLS scores, see how web fonts affect Core Web Vitals and layout shift.

There is also a correctness benefit. If a static bold file fails or arrives very late, browsers synthesize bold by thickening the regular outlines, which looks smeared and has different widths from the real bold, causing another shift when the real file arrives. A variable font with a declared weight range never needs synthesis.

FactorStatic fontsVariable font
Swaps per page loadOne per weight/style fileOne per file (often just one)
Risk of faux boldYes, if a weight is missing or lateNo, with a weight range
Preload strategyChoose which weights to prioritizePreload one file
Layout shift from staggered swapsCommonEliminated

Caching

Both approaches cache equally well when served with long-lived, fingerprinted URLs. The difference is in how changes and page variety affect hit rates:

  • With static fonts, a visitor who lands on a page that only uses regular weight caches one file; the next page uses bold and needs another download.
  • With a variable font, the first page caches everything, so later pages need no font downloads at all.

For sites where most visits are a single page, that matters less. For apps and content sites with multi-page sessions, the variable font's "download once, use everywhere" behavior helps subsequent navigations.

Rendering and CPU Cost

Variable fonts require the rasterizer to compute outlines for the requested axis values. In theory that costs more than rendering a precomputed static outline. In practice:

  • Browsers cache the computed glyphs for each instance, so the interpolation cost is paid once per weight in use, not per glyph drawn.
  • On current desktop and mobile hardware, the difference in text rendering time is negligible for normal pages.
  • Rendering only becomes noticeable when you animate axes, because each frame needs new outlines, layout, and paint.

So rendering cost should not drive your decision for static styling. It matters only for heavy font animation.

Decision Matrix

Your situationFaster choice
One weight onlyStatic
Regular + bold only, no other weightsEither; measure (static often slightly smaller)
Three or more weightsVariable
You need width or optical size variationVariable
You need non-standard weights (450, 650)Variable
Variable file has axes you do not useVariable after instancing, or static
Very slow networks, single-page visits, one weightStatic (or a system font stack)

Benchmarking Your Own Font

Rules of thumb are a starting point. The reliable answer comes from building both options from the same source and comparing them. This script takes a variable TTF, builds static WOFF2 instances for the weights you use, builds a partially instanced variable WOFF2 and a full variable WOFF2, all with the same Latin subset, and prints the sizes:

#!/usr/bin/env bash
set -euo pipefail

VF="Brand-Variable.ttf"           # source variable font
WEIGHTS=(400 500 600 700)         # weights your site uses
EXTRA_PINS=""                     # e.g. "opsz=drop" if the font has other axes
UNICODES="U+0000-00FF,U+0131,U+0152-0153,U+02C6,U+02DA,U+02DC,U+2000-206F,U+20AC,U+2122,U+2212"

mkdir -p bench

subset() {
  pyftsubset "$1" --unicodes="$UNICODES" --layout-features="*" \
    --flavor=woff2 --output-file="$2"
}

total_static=0
for w in "${WEIGHTS[@]}"; do
  fonttools varLib.instancer "$VF" wght="$w" $EXTRA_PINS -o "bench/static-$w.ttf"
  subset "bench/static-$w.ttf" "bench/static-$w.woff2"
  size=$(wc -c < "bench/static-$w.woff2")
  total_static=$((total_static + size))
  printf "static %-4s %8d bytes\n" "$w" "$size"
done

min=${WEIGHTS[0]}
max=${WEIGHTS[${#WEIGHTS[@]}-1]}
fonttools varLib.instancer "$VF" wght="$min:$max" $EXTRA_PINS -o bench/var-range.ttf
subset bench/var-range.ttf bench/var-range.woff2
subset "$VF" bench/var-full.woff2

printf "static total  %8d bytes (%d files)\n" "$total_static" "${#WEIGHTS[@]}"
printf "var %s-%s   %8d bytes\n" "$min" "$max" "$(wc -c < bench/var-range.woff2)"
printf "var full      %8d bytes\n" "$(wc -c < bench/var-full.woff2)"

Install the tools first:

pip install fonttools brotli

Run it with your real weights. If the variable range file is smaller than the static total, the variable font wins on bytes. If it is within 10–15%, the single swap and single request usually make the variable font the better choice anyway.

Measure on a real page

Byte counts are not the whole story. Build two versions of a representative page, one per approach, and compare with lab and field tools:

for variant in static variable; do
  for run in 1 2 3; do
    lighthouse "http://localhost:3000/compare/$variant" \
      --only-categories=performance --throttling-method=devtools \
      --output=json --output-path="./bench/$variant-$run.json" \
      --chrome-flags="--headless=new" --quiet
  done
done

for f in bench/*-*.json; do
  jq -r --arg f "$f" '"\($f)\tLCP \(.audits["largest-contentful-paint"].numericValue | floor)\tCLS \(.audits["cumulative-layout-shift"].numericValue)"' "$f"
done

Compare the medians of LCP and CLS. Also record both variants in the DevTools Performance panel with Slow 4G and 4× CPU throttling, and count the layout shifts that line up with font arrivals.

A Note on Google Fonts

When you request specific weights of a variable family from the Google Fonts API, for example wght@400;700, the CSS it returns often points every weight at the same variable file. Check the response: if all the src URLs are identical, you are already downloading the variable font, so requesting a range such as wght@400..700 costs nothing extra and gives you every weight in between.

Self-hosting gives you more control: you can instance the exact range you need, subset precisely, and preload the result.

Variable vs. Static Fonts FAQ

No. With one weight, a static file is smaller and faster. With two weights the result is close. From three weights upward, a variable font with only the axes you need is usually smaller and also reduces requests and swaps.

It probably includes axes you do not use, a wider weight range than you need, or a larger character set. Use the fontTools instancer to drop unused axes and narrow the weight range, then subset to the characters you serve before comparing again.

They can. Because all weights come from one file, everything swaps in a single frame instead of in several staggered swaps as each static weight arrives. They also prevent faux bold, which otherwise causes an extra shift when the real bold file loads.

It makes extra requests cheaper but not free. Each file is still a separate high-priority fetch, a separate preload decision, and a separate swap. Fewer files remain simpler and more predictable to load.

Not noticeably for static text on modern devices, because browsers cache the computed glyphs for each weight in use. Rendering cost only becomes significant when you animate axes continuously.

Run a benchmark first. If the instanced variable file is about the same size as your two static files, the single swap and the freedom to use intermediate weights are good reasons to switch. If it is much larger, staying static is reasonable.

Conclusion

The performance difference between variable and static fonts comes down to arithmetic and loading behavior. A variable file is larger than one static weight but usually smaller than three or more combined, and every unused axis tips the balance back toward static. Beyond bytes, a variable font gives you one request, one preload, one swap, and no faux bold, which often matters more for layout shift than a few kilobytes do.

Do not decide from a rule of thumb alone. Build both versions from the same source with the same subset, compare their sizes, and test a real page under throttling. For most sites that use a family at three or more weights, a properly instanced and subset variable font comes out ahead. For sites that use a single weight, static files or a system stack remain the leanest option.

Here are some useful references for going deeper on variable and static font performance:

  1. web.dev: Introduction to variable fonts on the web — how variable fonts are loaded and when they reduce payload.
  2. web.dev: Best practices for fonts — font delivery, preloading, and rendering guidance.
  3. fontTools: varLib.instancer documentation — partial instancing and pinning axes to reduce variable font size.
  4. fontTools: subset documentation — pyftsubset options for character sets, layout features, and WOFF2 output.
  5. Google Fonts Knowledge: Introducing variable fonts — the design and technical background of variable font files.
Share :

Related Posts

Ascenders, Descenders, and Baselines: The Anatomy of a Letterform

Ascenders, Descenders, and Baselines: The Anatomy of a Letterform

You align an icon next to a button label and it looks a pixel or two too high, no matter how you adjust vertical-align. You set overflow: hidden

Continue Reading
Are Google Fonts GDPR-Compliant?

Are Google Fonts GDPR-Compliant?

In late 2022, thousands of small business owners in Germany and Austria opened letters demanding a few hundred euros in "damages" because their websi

Continue Reading
How to Audit Web Font Performance with Lighthouse?

How to Audit Web Font Performance with Lighthouse?

A client sends you a screenshot of their PageSpeed Insights report: performance score 61, LCP 3.9 seconds, and a vague list of warnings. They want to

Continue Reading