We developed a font loader plugin for Gatsby
In this article, I will explain how I developed a font loader for Gatsby and how to apply it to work with both web fonts and self-hosted fonts.
In the past few weeks I was familiarizing myself with Gatsby and I was making a list of plugins that are good-to-have while working on a project. I went through the SEO plugins, SVG plugins, data source plugins, and finally, I wanted to check out some font loader plugins when I noticed something strange.
Here are the top two Gatsby plugins for fonts.
And if we open any of those two, we are greeted by the following message and README .
Most-used plugins are abandoned, deprecated and not actively maintained.
Don't get me wrong, it's totally okay for authors and companies to back down from developing and maintaining an open-source repo. I think it would be better to transfer the ownership to someone who wants to continue the development beforehand rather than leaving it as is and archiving it.
Asynchronous font loading
That missing support for the most-used plugins was my primary motivation to develop the font loader plugin, but what do font loaders even do?
In short, font loaders help eliminate render-blocking resources, in this case, fonts and their respective CSS files. I've gone into more detail about render-blocking resources in one of my previous articles.
When a website document is loaded, it automatically starts downloading high-priority resources that are needed before displaying the page - CSS, JavaScript, images, iframe, videos, fonts... Depending on how we instruct the browser, we can load some of those resources after the page has been displayed (rendered). By doing this, we are displaying the content as fast as possible to the user and loading all non-critical resources afterward to speed up the loading process.
This is especially true for web fonts, like Google fonts for example. During our page load, we are requesting a font CSS file from Google servers which also requests additional font files from Google CDN. Our page is not displayed until this chain of request resolves which can take some time depending on the CDN performance and user's internet connection.
With asynchronous loading, we can give a low priority to the font file and CSS and load it after the page is displayed. Although this has improved site performance, we have created a minor visual issue - Flash of Unstyled Text (FOUT).
Flash Of Unstyled Text (FOUT)
If the font is loaded after page content is displayed, we can see the moment the font changes between the fallback (default) font and the main web font that has been loaded asynchronously. This event is called Flash Of Unstyled Text or FOUT, for short. This change might even affect the page layout, size of some elements and even cause some visual bugs because the page is styled with web font in mind.
What we can do to make this effect much less noticeable is:
- Choose the fallback font that looks as closely as possible to the web font that is being loaded asynchronously
- Adjust font size, line height, letter spacing and word spacing to match the web font as closely as possible
After adjusting the fallback font CSS, we get the following result.
You might be asking: how can we detect when the font has been download and applied to the document?
We'll have to use JavaScript to detect that event. In case of Gatsby, I've written a plugin that both loads the web font asynchronously and it listens to font load event and applies a CSS class to HTML body element to handle FOUT.
Gatsby omni font loader plugin
During the past week, I've been working on creating a Gatsby plugin that will use the recommended way of loading fonts and enable devs to handle FOUT easily.
And few days ago, I've published Gatsby Omni Font Loader that can work with both web fonts and self-hosted fonts, add preload and preconnect on SSR, add font asynchronously, and handle FOUT. All in one small, neat package.
You can check out the source code on Github. Feel free to submit issues, feature requests and pull requests. Support and contribution are very much appreciated!
Installation
Start by installing the plugin with NPM or Yarn.
npm install --save-dev gatsby-omni-font-loader
or
yarn add --dev gatsby-omni-font-loader
Configure the plugin
In gatsby-config.js
file, reference the gatsby-omni-font-loader
plugin in the plugins
array and configure it.
Below is the sample config and explanation for each of the options available.
{
/* Include plugin */
resolve: "gatsby-omni-font-loader",
/* Plugin options */
options: {
/* Enable font loading listener to handle FOUC */
enableListener: true,
/* Preconnect URL-s. This example is for Google Fonts */
preconnect: ["https://fonts.gstatic.com"],
/* Self-hosted fonts config. Add font files and font CSS files to "static" folder */
custom: [
{
/* Exact name of the font as defied in @font-face CSS rule */
name: ["Font Awesome 5 Brands", "Font Awesome 5 Free"],
/* Path to the font CSS file inside the "static" folder with @font-face definition */
file: "/fonts/fontAwesome/css/all.min.css",
},
],
/* Web fonts. File link should point to font CSS file. */
web: [{
/* Exact name of the font as defied in @font-face CSS rule */
name: "Staatliches",
/* URL to the font CSS file with @font-face definition */
file: "https://fonts.googleapis.com/css2?family=Staatliches",
},
],
},
}
Handling FOUT
When enableListener: true
is set in plugin config in gatsby-config.js
, HTML classes are being added to <body>
element as the fonts are being loaded.
HTML class name format will be in the following format
wf-[font-family-name]--loaded
You can use the Font Style Matcher to adjust the perfect fallback font and fallback CSS config and use the styles from there.
Here is the example of how body element will look like after all fonts are being loaded (depending on the config).
<body class="wf-font-awesome-5-brands--loaded wf-font-awesome-5-free--loaded wf-staatliches--loaded">
So the CSS will look something like this
body {
font-family: "Merriweather", Georgia, sans-serif;
/* default styles */
}
body:not(.wf-merriweather--loaded) {
/* fallback font (Georgia) CSS config */
/* line-height, letter spacing, font-size... */
}
body:not(.wf-merriweather--loaded) h1 {
/* fallback font (Georgia) CSS config */
/* line-height, letter spacing, font-size... */
}
.wf-merriweather--loaded {
/* web font CSS config */
/* line-height, letter spacing, font-size... */
}
.wf-merriweather--loaded h1 {
/* web font CSS config */
/* line-height, letter spacing, font-size... */
}
Thank you for taking the time to read this post. Feel free to share your thoughts about this topic in the comments or drop us an email at hello@prototyp.digital. 😄