How to load your custom fonts in a smart way

2019-06-01 · 18 min read

Nowadays, the size of the webpage is bigger than before. Thanks to the improvement of bandwidth, it is fast to load everything even you have large images in the site.

However, this may harm your user's surfing experience. One of the hurdles is your fancy fonts.

The Problem

For example, you have a multi-language website and each language has its own custom fonts, for example, English and Japanese. The user would need huge bandwidth to load your site. Moreover, they will see "flash of invisible text" or so-called FOIT.

How to solve this problem?

1. Use modern font type (woff and woff2)

First, review your css files which defined font-face. It should be something like this:

/* roboto-regular - latin */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  src: url('../fonts/roboto-v19-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
       url('../fonts/roboto-v19-latin-regular.woff') format('woff'), /* Modern Browsers */
       url('../fonts/roboto-v19-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
       url('../fonts/roboto-v19-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
       url('../fonts/roboto-v19-latin-regular.svg#Roboto') format('svg'); /* Legacy iOS */

Now you can see, there are lots of font type in order to support different browsers. woff and woff2 for modern browsers, truetype and embedded-opentype for legacy browsers etc.

Actually, the loading order of font is based on the order in src. That means, if you want the browser to load truetype first, you can put it at the top. In this example, the browser will load eot first.

Unfortunately, eot is not optimized like woff and woff2, i.e. not compressed. It is better to always load woff or woff2, both are supported by modern browsers and have built-in compression. With compression, their file size is relatively smaller than eot etc.

For this example, better to change to:

/* roboto-regular - latin */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  src: url('../fonts/roboto-v19-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
       url('../fonts/roboto-v19-latin-regular.woff') format('woff'), /* Modern Browsers */
       url('../fonts/roboto-v19-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
       url('../fonts/roboto-v19-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
       url('../fonts/roboto-v19-latin-regular.svg#Roboto') format('svg'); /* Legacy iOS */

The browser will automatically fallback to next entry if it does not support woff2.

2. Use font-display to load faster

To prevent "flash of invisible text", better to use font-display in your stylesheet.

font-display: auto;
font-display: block;
font-display: swap;
font-display: fallback;
font-display: optional;

In default, the browser uses auto, and normally means font-display: block, this setting will block browser to load text before finished loading custom fonts. That is why the user will see a flash of unstyled text.

The other settings swap, fallback and optional have different block periods, so you can choose the one suits you.

For example, you have a header using a custom font and you just want to show it in custom fonts, you can use fallback. The browser will hide the text for about 100ms and, if the font has not yet been downloaded, will use the fallback text.

Think carefully about what you need, then you should be able to discover the best option for your website.

If you are using Google fonts, now you can set font-display by adding &display=<your-display-type> at the end of Google Fonts link. For example:

3. Unicode-range subsetting

For a website which has different languages, for example, a single page app (SPA) with Japanese and English version, normally both have different font-face. It is a waste if you open an English page but the browser also loads Japanese custom fonts, right? How can we solve this issue?

/* Japanese fonts example */
@font-face {
  font-family: 'Noto Sans JP';
  font-style: normal;
  font-weight: 400;
  src: url('../fonts/noto-sans-jp-v23-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
       url('../fonts/noto-sans-jp-v23-latin-regular.woff') format('woff'), /* Modern Browsers */
       url('../fonts/noto-sans-jp-v23-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */

The solution: unicode-range. If you set the unicode range, the browser will load the most suitable fonts for each language. Normally, English should be unicode-range: 0020—007F. For more languages, you can check it in unicode-table.

4. Host your fonts locally or in CDN

One of the bottlenecks of loading fonts is speed. If you need to call your fonts from another server, like Google Fonts, the page may need more time to load. Use Google Font Helper to download all font files and host it your own.

When your fonts load faster, the chance of getting FOIT should be lesser.

5. Preload your fonts

A minor that most of the developers forget is the preload tag. To make your fonts load faster, you can add preload to your font files. Unfortunately, this trick is not available in all browsers, like Firefox, is still not available to use preload. For more details, view

<link rel="preload" href="fonts/cicle_fina-webfont.woff2" as="font" type="font/woff2" crossorigin>


Hope you enjoy this post! Welcome to share your thoughts or tricks on web performance :)

Read More

  1. Web Font Optimization
  2. font-display - MDN
  3. font-display - CSS-Tricks

Written by Yuki Cheung

Hey there, I am Yuki! I'm a front-end software engineer who's passionate about both coding and gaming.

When I'm not crafting code, you'll often find me immersed in the worlds of Fire Emblem and The Legend of Zelda—those series are my absolute favorites! Lately, I've been delving into the fascinating realm of AI-related topics too.