HomeArticles

The Quiet Cost of Fancy Fonts and Heavy Images

Last week I was profiling a client's site that took 9 seconds to load on a mid-range Android phone. The usual suspects were there – unoptimized hero images, a carousel plugin that pulled in jQuery twice – but the real culprit was something I rarely see discussed in performance blogs: the font stack.

The page was loading five webfonts in four weights, each as a separate WOFF2 file, plus a few legacy formats for older browsers. That's roughly 1.2 MB of font data before any content even painted. And because the CSS didn't use font-display: swap, the browser held back text rendering for nearly 3 seconds. The images, ironically, were fine.

Why Images Get All the Attention

Images are easy to measure. You open DevTools, look at the Network tab, and see a 2.4 MB JPEG staring back at you. You resize, compress, switch to WebP, and boom – you've cut 70% of the weight. It's satisfying because it's visible.

Fonts are trickier. They're binary files with no obvious dimensions, and their performance impact is often hidden behind the FOUT/FOIT debate. But here's the thing: a font file can easily weigh more than a well-optimized image. And unlike images, fonts block rendering by default.

What Actually Worked

I'm not going to tell you to ditch custom fonts entirely – that's unrealistic. Instead, I applied a few pragmatic steps that any developer can do:

  • Audit your font usage. I found we were loading a bold weight that was never used anywhere on the site. Removing it saved 80 KB.
  • Use font-display: swap. It's a one-liner that prevents invisible text. Users see fallback fonts for a split second, which is better than a blank screen.
  • Preload the most critical font. If your brand font is used in the hero heading, preload it so it's ready before the CSS parses.
  • Consider variable fonts. One file can contain multiple weights, which is a huge win if you need several.
  • Serve local fonts when possible. Self-hosting avoids the extra DNS lookup and TLS handshake to a third-party CDN. It also gives you full control over caching.

But sometimes you don't have the time or the design freedom to optimize every font manually. That's where a curated catalog like Hey Fonts comes in handy – it lets you browse and download free fonts without the guesswork, so you can pick a font that's already optimized for the web and avoid the bloat of a 20-weight family.

Images: The Other Half of the Pie

Fonts are now my first check, but images are still a close second. The biggest wins I've had recently:

  • Using srcset and sizes attributes to serve appropriately sized images for different viewports.
  • Converting all decorative images to AVIF (or at least WebP) – the savings are often 50%+.
  • Lazy-loading everything below the fold with native loading="lazy".
  • For hero images, I now use a tiny placeholder that blurs or fades in – it avoids the layout shift and gives a visual cue that the page is loading.

One thing I stopped doing is relying on CDN image resizing services. They're convenient, but they add an extra request to a different domain, which can hurt performance on slow connections. If you have a simple static site, it's often faster to just resize images locally and commit them.

Tooling That Makes It Easier

I'm a fan of doing things by hand, but there's no shame in using tools to automate the boring parts. For images, sharp is my go-to for batch processing. For fonts, I use fonttools to subset and strip unused glyphs – a single Google Font can go from 200 KB to 40 KB that way.

If you're building with a static site generator, you can integrate these steps into your build process. I have a simple Node script that scans my CSS for font-family declarations, downloads the fonts locally, subsets them, and outputs a fonts.css with the right @font-face rules. It's about 100 lines of code, but it saves me hours every time I switch themes.

Testing, Not Guessing

Finally, don't just trust your gut. Use Lighthouse or WebPageTest to see how fonts and images affect your Core Web Vitals. I was surprised to find that a single font file was the reason my Largest Contentful Paint was 2.5 seconds – not the hero image I'd been ignoring.

Once I fixed the fonts, the LCP dropped to 1.1 seconds. The images were already fine. It's a reminder that performance is a holistic game – and that fonts deserve as much attention as the images they sit next to.

So next time you're profiling a slow site, don't just look at the .jpg files. Open the font inspector, check the number of requests, and see if you can trim the fat there. Your users will thank you – even if they can't quite put their finger on why the page feels snappier.