Skip to main content
🍞
Dev Corner
Optimize Web Fonts For Page Speed

Optimize Web Fonts For Page Speed

Fonts are a crucial part of every app or website, and they play a significant role in front-end performance. However, making sure you have a fast web font is a practice usually overlooked.

Optimize Web Fonts For Page Speed

Using a fast eCommerce API alone does not guarantee a fast experience for the user, especially if you are using external fonts the wrong way. For a better user experience overall, optimizing page performance and design loading fonts you use fast is a must.

How can you do that? Well, read on to find out how to reduce the font byte footprint up to 90% to improve site speed and FCP.

Motivation

With the evolution of web browsers, new features are coming every day, and today, I would like to talk about one that, although small, may impact the performance of the website/app.

I always strive to improve the performance of the website or application that I’m working on. Recently, I came across a great article over at Smashing Magazine about "Optimizing Google Fonts Performance" by  Danny Cooper. So, based on it, I ran some tests on my own to see the results and take my conclusions.

The first thing that I did was use the browser console to see the sizes of each download and see how it would impact.

For the test, I’ve used the Roboto font since it’s one of the most used fonts ATM, with a lot of variants, 12, to be more precise! Being them:

  1. Thin
  2. Thin Italic
  3. Light
  4. Light Italic
  5. Regular
  6. Regular Italic
  7. Medium
  8. Medium Italic
  9. Bold
  10. Bold Italic
  11. Black
  12. Black Italic

And the API that I’ll use for the test is the Google Fonts API.

The Tests

All right, so let’s get started.

First, I’ve started by asking for the font family Roboto: 

fetch("https://fonts.googleapis.com/css?family=Roboto")

Which turn to be the normal font style with the weight of 400 for the following languages:

  • latin 
  • latin-ext 
  • vietnamese 
  • greek 
  • greek-ext 
  • cyrillic 
  • Cyrillic-ext

The measurements I’ve got were:

Then, let's try all of the 12 variants.

fetch("https://fonts.googleapis.com/css?family=Roboto:100,100i,300,300i,400,400i,500,500i,700,700i,900,900i")

Now, let’s say I only need the variants regular (400) and the bold (700) because they will be the only ones that I’ll use:

fetch("https://fonts.googleapis.com/css?family=Roboto:400,700")

So, it’s possible to see the bundle sizes and the times to download them are decreasing! 

That’s awesome. However, that is one more thing that we can do. Let’s say I’ll use only the Roboto font for the title of the website/application. Then I could ask for those characters and reduce the bundle size and time to download even more:

fetch("https://fonts.googleapis.com/css?family=Roboto:700&text=xcompany")

So what is happening here? Let’s try to make things more clear. So, for each variant, I ask the API, and the response is something like this:

@font-face {  font-family: 'Roboto';  font-style: normal;  font-weight: 700;  src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/l/font?kit=KFOlCnqEu92Fr1MmWUlvBh0_IsHAncsWGhbabilm&skey=c06e7213f788649e&v=v19) format('woff2');}

As you can see, a new HTTP request is coming with the font face, so the browser still needs to fire another request to get the font! And that’s why the time varies for each request, although the resource size is smaller.

Now, imagine that we need to fire this for each font variant. The requests will be sequential, meaning they will only be fulfilled after completing the previous one!

To speed up things more and don’t block every request, two things can be done.

DNS Prefetch and Preconnect

DNS prefetch and preconnect scripts will allow the browser to start the connections with Google Fonts API as soon as the page loads!

<link rel="dns-prefetch" href="//fonts.googleapis.com">

And the 

<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>

This will allow the browser to start the additional necessary requests for each font variant at the same time as the first one starts, instead of waiting until the first request finishes to start the other ones!

@font-face descriptor (currently defined as font-display) that allows control over how a downloadable font renders before it is fully loaded.

Cool! Now that I’m getting the fonts from the Google Font API without blocking the requests, I only have to rely on the internet connection and hope the requests will be fulfilled.

Question: When the browser retrieves the font(s), will the user see something? Some text?

Well, maybe not, but I can solve that with one thing: display=swap. All I need is to add this query parameter to the request, and modern browsers will swap the font to the system default while the specific font is being loaded!

Google Fonts API will return this:

@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

See the font-display: swap? That is what will tell the browser to swap the font to the fallback while fetching the Roboto one.

Go to Can I Use and search for font-display. We are presented with this description: The font-display descriptor determines how a font face is displayed based on whether and when it is downloaded and ready to use.

And all the major browsers already supported it.

CSS font-display definition.

This is a great feature, or at least for me, it was the wow factor because users will have some context to see while the specific font is being loaded.

This is all cool, but one could ask if it wouldn’t be better to keep the fonts I want and serve them myself. 

We can. Previously, I was doing it this way, but then I read more about it and realized I would have more work on my end, like keeping the fonts up-to-date, checking for new releases, and keeping several font formats for different browsers.

Why doing this while the browsers can do it for us?

The Google Fonts API at the time of font request will check out the user-agent to know the best font format that can be sent back. 

Based on that, the API will send the highest compressed font format, which can save some bytes, whether it is a woff2, woff, eot or ttf format! And it will also provide the most up-to-date font.

The only thing that can go wrong is that the request is not fulfilled because of the internet connection (or lack of it), but the browsers keep fonts in the cache so that they can use them, or they will use the fallback since the font-display will swap!

Async Load Fonts

There is a trick that can be done to have the fonts loaded asynchronously to avoid chaining requests, as can be found in the Page Speed Insights from Google.

<link href="https://fonts.googleapis.com/css?family=Roboto+Slab:700|Roboto:400,500,700,900&amp;display=swap" rel="stylesheet" media="print" onload="this.media='all'">

It's simply having the link to the Google fonts API set to media "print" and changing it as soon as the load finishes to "all" since we want the styles to be applied to the screen environment. That is done with the `onload="this.media='all'"` the `onload` attribute that will be executed immediately after a page has been loaded, resulting in no chaining requests!

👍Best Practices for Font Optimization

Utilize Moderation. Use custom fonts sparingly to maintain a balance between aesthetics and page load time.
Mobile Optimization. Reduce font load on mobile devices using HTML media attributes and CSS @media queries to conserve page weight on slower networks.
Employ System Fonts. Opt for system or web-safe fonts, which don't require additional HTTP requests, thereby boosting page speed.
Asynchronous Font Loading. As I already mentioned, apply asynchronous loading techniques to prevent render-blocking, ensuring fonts don't hinder page rendering and user interaction.
Font Display Strategies. Utilize font-display property to control how fonts are displayed during the loading process, enhancing perceived performance.
Self-Hosting Fonts. When possible, self-host fonts to reduce reliance on external resources and have better control over font loading and caching.
Optimize Font Files. Compress font files and use modern formats like WOFF2 to reduce file size without compromising quality.
Preloading and Prefetching. Again, I talked about it in the above example. Use resource hints preload and prefetch to prioritize the loading of crucial font files, ensuring they're available as soon as needed.
Font Subsetting. Subset fonts to include only the characters and icons used on the website, reducing the file size and speeding up loading times.

Conclusion

It’s clear that considering the download of the Roboto font (weight 400) vs. (weight 700 and the needed text), we are facing a reduction of 88.13%! And even more when going with (weights 400 and 700), 94.2% reduction. As you can see, it has a significant impact on frontend performance, improving your site speed and, specifically, first contentful paint (FCP).

In today’s speed-obsessed world, every byte matters.

Follow the Rabbit🐰

Web Safe Fonts

Web Safe Fonts

Every web page we visit on the internet has text to explain something, give us indications, or tell us a story. They can also be fundamental in design and branding. As we can see, typography is essential on the web, and it's also something we should consider when building a website or application for many reasons, like availability, readability, and performance.

But first, let's cover some basic fonts theory.

Frontend Performance Measuring, Monitoring & KPIs

Frontend Performance Measuring, KPIs, and Monitoring

Core Web Vitals, INP, LCP, Cumulative Layout Shift, FID, Webpagetest.org, Google Lighthouse, Pagespeed Insight, CrUX. We're sure you've stumbled upon one of these in your quest to improve front-end performance, right?

But how do you measure performance? And why do you need to be data-driven and continuously monitor, optimize, and build a performance testing and optimization culture?

Let's explore that. Let's go millisecond hunting.