⚔️ Code Conqueror

⌨️ Using Google Fonts with next.js

Nov 18, 2019

The web has come a long way, but getting your font to show up on your site without flashing and not blocking rendering can be tricky. Google Fonts now supports "font-display: swap;" which is a great way to make sure that your page loads and if for whatever reason the custom font doesn't load it falls back to a system or other local font.

How to get google fonts working in Next.js

For Next.js there are two main ways to load your font:

a) add the font locally

b) pick a font that can be loaded from a performant CDN like Google Fonts

For our example we'll use the font that's on this site Noto Sans. Adding the font locally in Next.js is as simple as adding it to the

public/
directory. eg in this case we added
/public/fonts/noto-sans-v9-latin-regular.woff2
which will automatically be picked up by next and served at
/fonts/noto-sans-v9-latin-regular.woff2
(without the public prefix). You can also serve any other static files from your Next.js application from this public directory. The second thing we have to do is add the font to the document (in
pages/_document.js
) head like:

// /pages/_document.js
import React from 'react';
import Document, { Html, Head, Main, NextScript } from 'next/document';
class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>
<link
rel="preload"
href="/fonts/noto-sans-v9-latin-regular.woff2"
as="font"
crossOrigin=""
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;

Then you can add the

@font-display
css instruction to set the base font in another part of your application. The exact location of the css might differ depending on what css-in-js solution you are using, but for the built-in styled-jsx one option is to simply create a global style tag that sets the font like:

// /pages/_app.js
import App from 'next/app';
class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<div>
<Component {...pageProps} />
<style jsx global>{`
@font-face {
font-family: 'Noto Sans';
src: url('/fonts/noto-sans-v9-latin-regular.woff2');
font-weight: bold;
font-style: normal;
font-display: swap;
}
`}</style>
</div>
);
}
}
export default MyApp;

We ran some performance checks of using the google api to load the font vs Zeit's CDN form the public folder, and they seemed about the same (although Google was a bit snappier in some regions).

Typography without Flash of Unstyled Text

You should note that when setting up typography a common issue is the so-called "flash of unstyled text". This is when the end-user can see some text before the actual font is loaded.

font-display: swap;
does have the drawback of flashing this unstyled text if the font has yet to load. There are several other strategies you might consider depending on how fast the CDN you are pulling the font from is a good summary can be found on CSS tricks.

Basically there are four strategies you need to consider:

  • block
    this means that we don't render anything to the end user while the font loads. Which can lead to a somewhat jarring experience and is especially bad when the network condition is bad
  • swap
    this is what we are using and is what we recommend for google fonts. Typically you won't see much flash of unstlyed text since the google CDN (or in our case the Zeit one) is quite fast.
  • fallback
    a choice we don't really recommend. It will show invisible text like
    block
    for a short time and if it doesn't find the font will fallback to eg an available system font. It would cut your unusable time though in the case of a poor network condition.
  • optional
    a good choice when the font isn't actually that important eg you are mostly focused on the content and not brand consistency. basically it's like fallback but if the connection is slow it may never even attempt to load your custom font.

Why google fonts

Google Fonts as really made it much easier for sites to get custom fonts up and running without much headache. The selection is quite vast and their CDN is fast. However you do need to be a bit careful about the font-display issues discussed above when building a site.

Really the reason to use google fonts is the CDN speed. You could easily load another custom font in the Next.js

public/
directory as we have done, but the Zeit CDN is somewhat lower coverage than google's so you will experience somewhat higher instances of the FOIT / FOUT issues.

A very good an extensive resource on font-loading can be found here by Zach Leat.

A great tool for getting the css

font-display
snippet for Google Fonts can be found at Google Webfont Helper. This has snippets for eg our Noto Sans like:

/* noto-sans-regular - latin */
@font-face {
font-family: 'Noto Sans';
font-style: normal;
font-weight: 400;
font-display: swap;
src: local('Noto Sans'), local('NotoSans'),
url('/fonts/noto-sans-v9-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('/fonts/noto-sans-v9-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
}

That can be used to target Noto Sans in modern browsers. They also have some support for older browsers as well if you need to support them.