Author, Dharsh Dharsh •  • 7 min read

Variable Web Fonts with NextJS and TailwindCSS
Learn the best way to optimize your fonts with NextJS and TailwindCSS!!

Variable Web Fonts with NextJS and TailwindCSS Cover

Fonts play an essential role in giving character to a website. But if you don't set them up correctly, they could hurt your website's performance and user experience. "Your font choice is critical for branding, readability and performance" - Lee Robinson. In this blog, I show you the best way to set up custom web fonts on your Next.js and TailwindCSS application. The core concepts still apply for applications that don't use this tech stack. This particular blog was inspired by Lee Robinson's blog on Web Fonts in 2021.

Why custom web fonts can be a nightmare

When using custom web fonts, you need to make sure you're loading the fonts effectively. Not doing so can lead to issues like Cumulative Layout Shift (CLS) due to Flash of Unstyled/Invisible/Faux Text. CLS is a shift in layout after the page is loaded that causes poor UX (ever have the text completely jump on you due to ads loading on an article? This jump can happen with font swaps as well).

I ran into CLS issues when trying to load fonts through Google Fonts. The page would load with a default font before quickly changing into the custom font once loaded. This swap in font would cause a slight jump in the layout because of the slightly different aspect ratios of the two fonts. It also didn't help that Google Fonts no longer supports cross-browser font caching.

System and Web fonts

The easiest and most efficient way to use Web fonts is not to use them. Browsers include web-safe fonts by default. You can also use the system font stack, fonts pre-installed on Apple, Microsoft and Google devices. In both these cases, you skip the need to download fonts to your client. These are good strategies, but that doesn't mean there isn't a way to use custom web fonts effectively.

Ideal solution for Web Fonts using Next.js and TailwindCSS

Here is a small list of optimizations that can lead to clean use of Web Fonts in 2021 based on this blog:

  • Use a variable font

  • Preload your font file

  • Self-host instead of Google Fonts

  • Use font-display: optional to prevent layout shift

  • Subset your imports

Now I will show you how I set up custom web fonts effectively on my website.

Step 1: Use Variable Fonts in WOFF2 Format

Variable Fonts are a great option if you are looking to load many different font weights and styles. They are a font type that allows you to have multiple font weights and styles imported through just one file. To learn more about variable fonts, check out this documentation.

The WOFF2 format has become the standard these days for font formats for the web. It is a web font format that is a lot smaller than others.

To use variable fonts, look for a variable font file for the font you're looking to use. The WOFF2 format is prefered. Most of the time, it is difficult to find a .woff2 format for variable fonts. Although, it is easiest to find a .ttf format. Using online converters like Convertio and ConverCloud to convert the TTF to WOFF2 seem to break the axis needed to interpolate between font variation settings. But luckily, there is a way to convert them without breaking anything. Here is an article that explains how to use this repository to compress TTF into WOFF2.

Next, add the font file(s) to your /public/fonts directory and import it as so.

@font-face {
  font-family: "iA Writer Quattro V";
  font-style: normal;
  font-weight: 100 900; // range of weight
  font-display: optional;
  src: url(/fonts/iAWriterQuattroV-Italic.woff2) format("woff2");
}

Here is an example of a variable font iA Writer Quattro V.

Step 2: Self Host Your Font

Google fonts recommend hosting your fonts for optimal performance as there are no real benefits to using the fonts hosted by them. If you're using Next.js, it's super easy to host your fonts. Just throw them in the /public/fonts directory of your application. You can also use HTTP headers to cache the font for faster loads. Add this configuration to your HTTP header.

cache-control: public, immutable, max-age=31536000;

This optimization ensures that your hosted font is cached for the public and kept immutable (not going to change) for 1 year.

Here is an example you could use if you're using Vercel. Add this to your /vercel.json.

{
  "headers": [
    {
      "source": "/fonts/iAWriterQuattroV-Regular.woff2", // use "/fonts/(.*)" to cache your entire fonts directory
      "headers": [
        {
          "key": "Cache-Control",
          "value": "public, max-age=31536000, immutable"
        }
      ]
    }
  ]
}

Step 3: Preload Font Resources

Preloading is a way to load critical resources early in the page load lifecycle. Preloading for fonts is a must to avoid Cumulative Layout Shift and decrease First Contentful Paint. Here is how you do it.

Add this to your <Head> tag. For Next.js, having a custom _document.tsx file if you don't have one already, might be helpful. Add multiple preloads for multiple files.

<Head>
  <link
    rel="preload"
    href="/fonts/iAWriterQuattroV-Regular.woff2"
    as="font"
    type="font/woff2"
    crossOrigin="anonymous"
  />
  <link
    rel="preload"
    href="/fonts/iAWriterQuattroV-Italics.woff2"
    as="font"
    type="font/woff2"
    crossOrigin="anonymous"
  />
</Head>

Step 4: Font Display Optimization

font-display is a parameter in the @font-face CSS property which chooses the strategy to render loaded web fonts with values such as auto, swap, block, fallback and optional. To prevent layout shift due to FOUT or FOIT, using the optional value seems to be the best choice. optional basically doesn't allow a swap in fonts once the page is loaded. This way, you avoid the chance of a layout shift due to font swaps, but there is a potential of the custom web font not showing up on a load if the font loading isn't optimized or the network is slow.

Below is an example of inserting a font file through a global CSS file. Add multiple font faces for multiple file imports.

@font-face {
  font-family: "iA Writer Quattro V";
  font-style: normal;
  font-weight: normal;
  font-display: optional;
  src: url(/fonts/iAWriterQuattroV-Regular.woff2) format("woff2");
}

@font-face {
  font-family: "iA Writer Quattro V";
  font-style: italic;
  font-weight: normal;
  font-display: optional;
  src: url(/fonts/iAWriterQuattroV-Italic.woff2) format("woff2");
}

If you wanted to use font-display: swap, look into font metrics override descriptors.

@font-face {
  font-family: ...;
  src: ...;
  ascent-override: 80%;
  descent-override: 20%;
  line-gap-override: 0%;
  ...;
}

Step 5: Subset Your Import

Some fonts can have multiple languages and glyphs, which can cause big file sizes. To reduce the burden, you can filter out only the required Unicode by doing so.

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900; // Range of weights supported
  font-display: optional;
  src: url(/fonts/inter-var-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}

Step 6: Extend TailwindCSS FontFamily

Finally, to be able to use font-family with TailwindCSS, add this to your /tailwind.config.json

module.exports = {
  ...
  theme: {
    extend: {
      ...
      fontFamily: {
        sans: ['iA Writer Quattro V', ...fontFamily.sans]
      },
      ...
    },
  },
  ...
}

Conclusion

Here is the ultimate recipe to use custom web fonts with Next.js and TailwindCSS

  • Use variable fonts in WOFF2 format

  • Self host your font instead and cache them

  • Preload your font resources

  • Use font-display: optional to prevent layout shift

  • Subset your font import

  • Extend your Tailwind FontFamily

I recommend checking out Lee Robinson for more content related to Next.js, UI/UX and best practices for web applications. He is currently the Head of Developer Relations at Vercel, a position I would love to hold someday. He's been a great inspiration in getting this website and blog up and running.

Don't think of your website as a self-promotion machine, think of it as a self-invention machine.

  • Austin Kleon