Preloading Font with Rel Preload

Preloading @font-face fonts?

This answer is no longer up to date

Please refer to this updated answer: https://stackoverflow.com/a/46830425/4031815



Deprecated answer

I'm not aware of any current technique to avoid the flicker as the font loads, however you can minimize it by sending proper cache headers for your font and making sure that that request goes through as quickly as possible.

preloading font with rel preload

Your preload-Tag takes the argument "crossorigin", which must be given for Webfonts, even if they are on the same host as your website.

See https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content#Cross-origin_fetches or https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/#early-loading-of-fonts .

How to apply link rel=preload to @font-face?

The recommendation is to apply the preload to your link tags, not in the style itself.

So for instance if your css code is in a file called "fonts.css", you would put the following line in the HTML where you are going to access it:

<link rel="preload" as="style" href="fonts.css">

You would then be telling the page to preload your "fonts.css" file. The "as" here should be whatever the actual resource is, so if instead of a style sheet you actually wanted to use a font file, just change the "as='style'" to "as='font'".

When to use rel=preload? Why is preloading fonts/FontAwesome a good idea?

You will often see this recommendation if you use @font-face to load fonts in.

To understand why you get this recommendation you have to consider how the browser receives and parses information.

  1. HTML is downloaded, browser looks at all the assets to download it found in the HTML and starts downloading them and parsing them.
  2. The browser discovers a CSS file and downloads it. When that CSS file has downloaded and been parsed your browser finds a reference to your 'font-awesome' font and then adds that to the list of things to download.
  3. The browser downloads that font, but a lot later than it is needed.

By adding preload to the item your order changes to HTML first, then CSS and the font-awesome font at the same time, meaning a key asset is loaded earlier.

Why is this important?

To understand why this is important you need to understand 'critical requests' - those are all the assets required in order to render the 'above the fold' content.

Above the fold content is content you can see without scrolling the page.

Now if you have any icon showing in this 'above the fold' content then your font-awesome font becomes part of your 'critical request chain' - i.e. content that is essential to paint everything above the fold.

By using preload you get the font delivered sooner (2 steps not 3 as illustrated earlier), so your above the fold content can be rendered sooner and so your site appears to load faster - this is a major factor in PSI scoring and real-world conversion rate improvements.

Should I use rel="preload" then?

In most circumstances yes you should if it is recommended, it reduces your critical request chain depth and normally results in faster loading times. However you do need to check your critical request chain to ensure it isn't a false positive (PSI isn't perfect).

The easiest way to check is to open developer tools, enable 3G throttling on the network tab and then see if the page displays faster with or without preload.

Is it my best option for the scenario given in the question?

In this example, no, but only because font-awesome is not a good idea in general.

What you really want to do is get rid of font-awesome entirely. Icon Fonts are an out-dated and terrible practice us web developers have adopted that just won't go away.

Instead of loading a 50kb+ file (for each 'weight' of font-awesome you use) plus 30kb of CSS why not use inline SVGs instead.

Inline SVGs have several advantages, but the key ones are:-

  1. As they are inlined in the HTML you remove at least one network request (normally 2-3) - great for speed.
  2. They are tiny - a typical icon is less than 1kb unzipped - with 10 icons you said you use that is 10kb total before zipping. Compare that to 180kb zipped for font-awesome fonts, CSS etc. and you can see the performance improvement.
  3. As you have inlined your icons within your HTML you reduce your 'critical request chain' length, so you can get to sub 1 second initial page renders (obviously you need to inline all of your critical CSS required for the above the fold as well.)
  4. The most important reason - people use custom stylesheets on your websites. For example people with dyslexia may prefer a certain font as it is easier to read, so they may force a site to use that font. Your beautiful 'font icons' become the dreaded 'missing character square box of doom' - which makes it really difficult to know what they are clicking. Accessibility is becoming more and more important!

Note on point 2 - the reason icon fonts are so large is they contain hundreds of icons. It is possible to reduce them to be slightly smaller that inline SVGs but that kind of optimisation often gets over-looked and is actually more time consuming than simply inlining and referencing SVGs. I just thought I would add this for completeness.

Preloading fonts in Chrome with 'preload' link directive

I've found the solution to this. Sadly, the W3C spec on preload links doesn't make this clear, and there seems to be a lot of confusion about this where programmers have posted questions about it, especially as regards the developer's console error message (Google that by itself to see how many people are getting it and are perplexed by it; it's indicative of how misunderstood this very useful directive is).

You cannot use the preload link by itself to load fonts. It must be used in conjunction with @font-face. Specifically, the preload link must precede the @font-face directive, and @font-face must occur soon after the preload link, if not immediately. It would seem that in the <head> section, the preload link should come last in your links, and then @font-face should immediately follow, either in a linked style sheet or in a <style> section that follows.

Here is a corrected version of the original code:

<!DOCTYPE html>
<html>
<head>
<title>Preload Font Test</title>
<link rel="preload" href="https://fonts.gstatic.com/s/muli/v10/zscZFkjVRGyfQ_Pw-5exXPesZW2xOQ-xsNqO47m55DA.woff2" as="font" crossorigin>
<link rel="preload" href="https://fonts.gstatic.com/s/muli/v10/YxNEAWILjDc466nftZdqXuvvDin1pK8aKteLpeZ5c0A.woff2" as="font" crossorigin>
<link rel="preload" href="https://fonts.gstatic.com/s/opensans/v13/k3k702ZOKiLJc3WVjuplzBampu5_7CjHW5spxoeN3Vs.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="https://fonts.gstatic.com/s/opensans/v13/PRmiXeptR36kaC0GEAetxv79_ZuUxCigM2DespTnFaw.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="https://fonts.gstatic.com/s/lato/v13/lEjOv129Q3iN1tuqWOeRBgLUuEpTyoUstqEm5AMlJo4.woff2" as="font" type="font/woff2" crossorigin>
<style>
@font-face{font-family:'Muli';font-weight:400;font-style:normal;src:url('https://fonts.gstatic.com/s/muli/v10/BkuZCUxEYxRfSu-XBj9_CA.eot');src:url('https://fonts.gstatic.com/s/muli/v10/BkuZCUxEYxRfSu-XBj9_CA.eot?#iefix') format('embedded-opentype'),
local('Muli Regular'),
local('Muli-regular'),
url('https://fonts.gstatic.com/s/muli/v10/zscZFkjVRGyfQ_Pw-5exXPesZW2xOQ-xsNqO47m55DA.woff2') format('woff2'),
url('https://fonts.gstatic.com/s/muli/v10/minRpKQdEvXRRS8oAbAtWvesZW2xOQ-xsNqO47m55DA.woff') format('woff'),
url('https://fonts.gstatic.com/s/muli/v10/BfQP1MR3mJNaumtWa4Tizg.ttf') format('truetype'),
url('https://fonts.gstatic.com/l/font?kit=5laWPvb-IgmGJk9r92y1Hw&skey=2b55aa3f2f059b75&v=v10#Muli') format('svg');}

@font-face{font-family:'Muli';font-weight:400;font-style:italic;src:url('https://fonts.gstatic.com/s/muli/v10/vurWTAYiHMPVScIey50dUQ.eot');src:url('https://fonts.gstatic.com/s/muli/v10/vurWTAYiHMPVScIey50dUQ.eot?#iefix') format('embedded-opentype'),
local('Muli Italic'),
local('Muli-italic'),
url('https://fonts.gstatic.com/s/muli/v10/YxNEAWILjDc466nftZdqXuvvDin1pK8aKteLpeZ5c0A.woff2') format('woff2'),
url('https://fonts.gstatic.com/s/muli/v10/DSOyST5zmfghBgRIogdupevvDin1pK8aKteLpeZ5c0A.woff') format('woff'),
url('https://fonts.gstatic.com/s/muli/v10/AQQ1r0_czneVCtTD9ckSEA.ttf') format('truetype'),
url('https://fonts.gstatic.com/l/font?kit=Ok1ULmeTg1kfss3jIu3xZQ&skey=f22af9a5d2e9fc47&v=v10#Muli') format('svg');}

@font-face{font-family:'Open Sans';font-weight:700;font-style:normal;
src:url('https://fonts.gstatic.com/s/opensans/v13/k3k702ZOKiLJc3WVjuplzHZ2MAKAc2x4R1uOSeegc5U.eot');
src:url('https://fonts.gstatic.com/s/opensans/v13/k3k702ZOKiLJc3WVjuplzHZ2MAKAc2x4R1uOSeegc5U.eot?#iefix') format('embedded-opentype'),
local('Open Sans Bold'),
local('Open-Sans-700'),
url('https://fonts.gstatic.com/s/opensans/v13/k3k702ZOKiLJc3WVjuplzBampu5_7CjHW5spxoeN3Vs.woff2') format('woff2'),
url('https://fonts.gstatic.com/s/opensans/v13/k3k702ZOKiLJc3WVjuplzKRDOzjiPcYnFooOUGCOsRk.woff') format('woff'),
url('https://fonts.gstatic.com/s/opensans/v13/k3k702ZOKiLJc3WVjuplzInF5uFdDttMLvmWuJdhhgs.ttf') format('truetype'),
url('https://fonts.gstatic.com/l/font?kit=k3k702ZOKiLJc3WVjuplzFlIn5tFQcqMuf-jhyJP0ps&skey=cd9e1a36bb25a3c3&v=v13#OpenSans') format('svg');}

@font-face{font-family:'Open Sans';font-weight:700;font-style:italic;src:url('https://fonts.gstatic.com/s/opensans/v13/PRmiXeptR36kaC0GEAetxrFt29aCHKT7otDW9l62Aag.eot');src:url('https://fonts.gstatic.com/s/opensans/v13/PRmiXeptR36kaC0GEAetxrFt29aCHKT7otDW9l62Aag.eot?#iefix') format('embedded-opentype'),
local('Open Sans Bold Italic'),
local('Open-Sans-700italic'),
url('https://fonts.gstatic.com/s/opensans/v13/PRmiXeptR36kaC0GEAetxv79_ZuUxCigM2DespTnFaw.woff2') format('woff2'),
url('https://fonts.gstatic.com/s/opensans/v13/PRmiXeptR36kaC0GEAetxhbnBKKEOwRKgsHDreGcocg.woff') format('woff'),
url('https://fonts.gstatic.com/s/opensans/v13/PRmiXeptR36kaC0GEAetxp_TkvowlIOtbR7ePgFOpF4.ttf') format('truetype'),
url('https://fonts.gstatic.com/l/font?kit=PRmiXeptR36kaC0GEAetxntNmQEE9wZ6UZlmiISB1pg&skey=7e5bb13249e84143&v=v13#OpenSans') format('svg');}

@font-face{font-family:'Lato';font-weight:900;font-style:normal;src:url('https://fonts.gstatic.com/s/lato/v13/BjDVcwQGWPX2RAidnkd0Bw.eot');src:url('https://fonts.gstatic.com/s/lato/v13/BjDVcwQGWPX2RAidnkd0Bw.eot?#iefix') format('embedded-opentype'),
local('Lato Black'),
local('Lato-900'),
url('https://fonts.gstatic.com/s/lato/v13/lEjOv129Q3iN1tuqWOeRBgLUuEpTyoUstqEm5AMlJo4.woff2') format('woff2'),
url('https://fonts.gstatic.com/s/lato/v13/G2uphNnNqGFMHLRsO_72ngLUuEpTyoUstqEm5AMlJo4.woff') format('woff'),
url('https://fonts.gstatic.com/s/lato/v13/4cKlrioa77J2iqTqBgkRWg.ttf') format('truetype'),
url('https://fonts.gstatic.com/l/font?kit=UxBsysUD4pfKXRb0IKmcRQ&skey=d01acf708cb3b73b&v=v13#Lato') format('svg');}

h1 {font-family:'Muli';font-weight:400;font-style:normal;font-size:1.5em}
h2 {font-family:'Muli';font-weight:400;font-style:italic;font-size:1.5em}
h3 {font-family:'Open Sans';font-weight:700;font-style:normal;font-size:1.5em}
h4 {font-family:'Open Sans';font-weight:700;font-style:italic;font-size:1.5em}
h5 {font-family:'Lato';font-weight:900;font-style:normal;font-size:1.5em}

</style>
</head>
<body>
<h1>This should be in Muli regular 400 -- and it is!</h1>
<h2>This should be in Muli Italic 400 -- and it is!</h2>
<h3>This should be in Open Sans Bold 700 -- and it is!</h3>
<h4>This should be in Open Sans BoldItalic 700 -- and it is!</h4>
<h5>This should be in Lato ExtraBold 900 -- and it is!</h5>
</body>
</html>

This works, and it does, in fact, collect your fonts as part of the initial navigation of the critical rendering path, which is the principal benefit. preload is currently only supported by Chrome and Opera, but other browsers will surely follow.

use preloaded bundled font in react

well did not find any ways around that, solved by uploading font to a corporate CDN

How to correctly preload a font whose filename is hashed, and whose style is in a CDN?

I found a solution, thanks to https://www.npmjs.com/package/@angular-builders/custom-webpack, and using a library for mMaterial Icons (https://www.npmjs.com/package/material-icons)

This builder allows to transform the index.html after the build process.

  1. Add classic preloading in index.html (works in dev)
<link rel="preload" href="/material-icons.woff2" as="font" type="font/woff2" crossorigin="anonymous">
<link rel="preload" href="/material-icons-outlined.woff2" as="font" type="font/woff2" crossorigin="anonymous">

  1. Add "indexTransform": "index-html-transform.js" in angular.json like this:
"configurations": {
"production": {
[...],
"indexTransform": "index-html-transform.js"
}
}

  1. Create index-html-transform.js:
const fs = require("fs");

/**
* @param {string} indexHtmlSource
* @return {string}
*/
const setMaterialIconsFontsPreloading = (indexHtmlSource) => {
const allOutputFilenames = fs.readdirSync("./dist/essai-preload-mat-icons");

const requiredMatIconsFontsMatches = indexHtmlSource.matchAll(/(material-icons.*)\.woff2/g);

/**
* @exemple `['material-icons', 'material-icons-outlined']`
*/
const requiredIconTypes = [...requiredMatIconsFontsMatches].map((match) => match[1]);

return requiredIconTypes.reduce((previousIndexHtml, requiredIconType) => {
/**
* @exemple `'material-icons-outlined.woff2'`
*/
const inputFilename = `${requiredIconType}.woff2`;

/**
* @exemple `'material-icons-outlined.125af8545b6.woff2'`
*/

const outputFilename = allOutputFilenames.find((outputFilenameItem) => outputFilenameItem.match(`^${requiredIconType}\\.\\w+\\.woff2$`));

return previousIndexHtml.replace(inputFilename, outputFilename);
}, indexHtmlSource);
}

/**
*
* @param {{ configuration?: string; project: string; target: string;}} targetOptions
* @param {string} indexHtmlSource
* @returns {string} The final index.html to serve.
*/
module.exports = (targetOptions, indexHtmlSource) => {
return setMaterialIconsFontsPreloading(indexHtmlSource);
};


Related Topics



Leave a reply



Submit