Optimize WebFont loading and rendering

Ilya Grigorik
Ilya Grigorik

A "full" WebFont that includes all stylistic variants,which you may not need, plus all the glyphs, which may go unused,can easily result in a multi-megabyte download.In this post you will find out how to optimize loading of WebFontsso visitors only download what they will use.

To address the problem of large files containing all variants,the@font-face CSS rule is specifically designedto allow you to split the font family into a collection of resources. For exampleunicode subsets, and distinct style variants.

Given these declarations,the browser figures out the required subsets and variants and downloads the minimal set required to render the text, which is very convenient.However, if you're not careful,it can also create a performance bottleneck in the critical rendering path and delay text rendering.

The default behavior

Lazy loading of fonts carries an important hidden implication that may delay text rendering.The browser mustconstruct the render tree,which is dependent on the DOM and CSSOM trees,before it knows which font resources it needs to render the text.As a result, font requests are delayed well after other critical resources,and the browser may be blocked from rendering text until the resource is fetched.

Font critical rendering path

  1. The browser requests the HTML document.
  2. The browser begins parsing the HTML response and constructing the DOM.
  3. The browser discovers CSS, JS, and other resources and dispatches requests.
  4. The browser constructs the CSSOM after all of the CSS content is received and combines it withthe DOM tree to construct the render tree.
    • Font requests are dispatched after the render tree indicates which font variants are needed torender the specified text on the page.
  5. The browser performs layout and paints content to the screen.
    • If the font is not yet available, the browser may not render any text pixels.
    • After the font is available, the browser paints the text pixels.

The "race" between the first paint of page content,which can be done shortly after the render tree is built,and the request for the font resource is what creates the "blank text problem"where the browser might render page layout but omits any text.

By preloading WebFonts and usingfont-display to control how browsers behave with unavailable fonts,you can prevent blank pages and layout shifts due to font loading.

Preload your WebFont resources

If there's a high probability that your page will need a specific WebFont hosted at a URL you know in advance,you can take advantage ofresource prioritization.Using<link rel="preload"> will trigger a request for the WebFont early in the critical rendering path,without having to wait for the CSSOM to be created.

Customize the text rendering delay

While preloading makes it more likely that a WebFont will be available when a page's content is rendered,it offers no guarantees.You still need to consider how browsers behave when rendering text that uses afont-family which is not yet available.

In the postAvoid invisible text during font loading you can see that default browser behavior is not consistent.However, you can tell modern browsers how you want them to behave by usingfont-display.

Browser Support

  • Chrome: 60.
  • Edge: 79.
  • Firefox: 58.
  • Safari: 11.1.

Source

Similar to the existing font timeout behaviors that some browsers implement,font-display segments the lifetime of a font download into three major periods:

  1. The first period is thefont block period.During this period, if the font face is not loaded,any element attempting to use it must instead render with an invisible fallback font face.If the font face successfully loads during the block period, the font face is then used normally.
  2. Thefont swap period occurs immediately after the font block period.During this period, if the font face is not loaded,any element attempting to use it must instead render with a fallback font face.If the font face successfully loads during the swap period, the font face is then used normally.
  3. Thefont failure period occurs immediately after the font swap period.If the font face is not yet loaded when this period starts,it's marked as a failed load, causing normal font fallback.Otherwise, the font face is used normally.

Understanding these periods means you can usefont-display to decide how yourfont should render depending on whether or when it was downloaded.

To work with thefont-display property, add it to your@font-face rules:

@font-face{font-family:'Awesome Font';font-style:normal;font-weight:400;font-display:auto;/* or block, swap, fallback, optional */src:local('Awesome Font'),url('/fonts/awesome-l.woff2')format('woff2'),/* will be preloaded */url('/fonts/awesome-l.woff')format('woff'),url('/fonts/awesome-l.ttf')format('truetype'),url('/fonts/awesome-l.eot')format('embedded-opentype');unicode-range:U+000-5FF;/* Latin glyphs */}

font-display currently supports the following range of values:

  • auto
  • block
  • swap
  • fallback
  • optional

For more information on preloading fonts, and thefont-display property, see the following posts:

The Font Loading API

Used together,<link rel="preload"> and the CSSfont-display give you a great deal of control over font loading and rendering,without adding in much overhead.But if you need additional customizations,and are willing to incur the overhead introduced by running JavaScript, there is another option.

TheFont Loading API provides a scripting interface to define and manipulate CSS font faces,track their download progress, and override their default lazyload behavior.For example, if you're sure that a particular font variant is required,you can define it and tell the browser to initiate an immediate fetch of the font resource:

Browser Support

  • Chrome: 35.
  • Edge: 79.
  • Firefox: 41.
  • Safari: 10.

Source

varfont=newFontFace("Awesome Font","url(/fonts/awesome.woff2)",{style:'normal',unicodeRange:'U+000-5FF',weight:'400'});// don't wait for the render tree, initiate an immediate fetch!font.load().then(function(){// apply the font (which may re-render text and cause a page reflow)// after the font has finished downloadingdocument.fonts.add(font);document.body.style.fontFamily="Awesome Font, serif";// OR... by default the content is hidden,// and it's rendered after the font is availablevarcontent=document.getElementById("content");content.style.visibility="visible";// OR... apply your own render strategy here...});

Further, because you can check the font status(via thecheck()) methodand track its download progress,you can also define a custom strategy for rendering text on your pages:

  • You can hold all text rendering until the font is available.
  • You can implement a custom timeout for each font.
  • You can use the fallback font to unblock rendering and inject a new style that uses the desiredfont after the font is available.

Best of all, you can also mix and match the above strategies for different content on the page.For example, you can delay text rendering on some sections until the font is available,use a fallback font, and then re-render after the font download has finished.

Note: The Font Loading API isnot available in older browsers. Consider using theFontLoader polyfill or theWebFontloader library to deliver similar functionality, albeit with even more overhead from an additional JavaScript dependency.

Proper caching is a must

Font resources are, typically, static resources that don't see frequent updates.As a result, they are ideally suited for a long max-age expiry—ensure that you specify both aconditional ETag header,and anoptimal Cache-Control policy for all font resources.

If your web application uses aservice worker,serving font resources with acache-first strategy is appropriate for most use cases.

You should not store fonts usinglocalStorageorIndexedDB;each of those has its own set of performance issues.The browser's HTTP cache provides the best and most robust mechanism to deliver font resources to the browser.

WebFont loading checklist

  • Customize font loading and rendering using<link rel="preload">,font-display, or the FontLoading API: default lazyloading behavior may result in delayed text rendering. These web platformfeatures allow you to override this behavior for particular fonts, and to specify custom renderingand timeout strategies for different content on the page.
  • Specify revalidation and optimal caching policies: fonts are static resources that areinfrequently updated. Make sure that your servers provide a long-lived max-age timestamp and arevalidation token to allow for efficient font reuse between different pages. If using a serviceworker, a cache-first strategy is appropriate.

Automated testing for WebFont loading behavior with Lighthouse

Lighthousecan help automate the process of making sure that you're following web font optimization best practices.

The following audits can help you make sure that your pages are continuing to follow web font optimization best practices over time:

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2019-08-16 UTC.