Published on March 7, 2025
Typography is an essential element of web development, impacting readability, UX, and brand identity. However, inadequate font optimization can lead to problems such as slow loading, layout shift, and poor readability.
If you're requesting external fonts, this can cause delays in rendering, which can lead to issues such as FOIT (flash of invisible text), where the text remains hidden until the font is fully loaded, or FOUT (flash of unstyled text), where a fallback font briefly appears and is then replaced by your chosen font. Both issues have a negative impact on UX and SEO.
This article assumes you are working with Next.js, and covers font optimization using the @next/font module, introduced in Next.js version 13+. We explain how you can use the @next/font
module to add and optimize your Google fonts or custom fonts in Next.js, and cover both vanilla Next.js and Next.js with Tailwind CSS use cases.
Self-hosting: The @next/font
module offers significant advantages over common font-loading methods like <link> tags and CSS @import. The most important of these is that it's self-hosting, meaning there's no need for external requests to third-party font hosting services like Google Fonts. Any third-party fonts get downloaded at build time and served to the user from your own server. This gives a faster initial page render, which will also help your page rank higher in Google search results.
Reduced CLS: Another benefit is the reduction in CLS (cumulative layout shift). Next.js fonts are now preloaded at build time, meaning that FOIT and FOUT can both be avoided, so there is generally no need for fallback fonts. On occasions when a fallback font is needed, it uses the CSS size-adjust property under the hood, so that the fallback fonts and the actual fonts have the same proportions, meaning that layout shift will be prevented either way. Since Google penalizes CLS as part of Core Web Vitals, using @next/font
will help with your SEO rankings.
Font subsetting: You can improve performance by specifying that you only need a certain subset of the font — for example, a specific language or character set.
Fonts are cached and preloaded at build time: This means Next.js fonts load much faster compared to alternatives.
There are two main alternatives to using Next.js fonts — the HTML <link> tag or the CSS @import rule — but these have a number of drawbacks when it comes to optimizing fonts:
Slow performance: Either of these will make your fonts much slower to use, due to the external request for the fonts, which needs to be received by the browser before it will render text. This causes lag that can make a website feel sluggish, especially for users on slow networks.
No automatic preloading: The lack of automatic preloading for these alternatives to Next.js fonts means the browser will be unable to speed up text rendering.
No built-in font subsetting: Unlike @next/font
, these traditional methods don't offer built-in font subsetting, meaning the entire font file will need to be downloaded, slowing performance.
FOIT issues: Neither <link>
nor @import
can prevent FOIT out of the box. You can try to avoid FOIT by using font-display:swap
to load a fallback font
; however, this just replaces FOIT with FOUT. The @next/font
module works much more seamlessly through preloading fonts and its use of the size-adjust property.
Lack of GDPR compliance: When using third-party fonts (for example, from Google Fonts), the alternative methods send the user's IP address to third-party servers, which could be a GDPR violation without the explicit consent of the user. This has already led to a small fine (and the threat of a much larger one) for one German company that was directly requesting fonts from Google Fonts. You don't have this issue with Next.js fonts, and it doesn't matter if you're using next/font/local or next/font/google — Next.js downloads the fonts at build time and they are served from your own server.
In the end, Next.js fonts are the best choice to optimize fonts since they enhance performance, user experience, accessibility, and privacy, while fixing common problems coming from <link>
and @import
. This provides preloading, font subsetting, and better control over rendering, for a faster and more stable website.
In fact, Next.js is a good all-round framework for optimizing content-heavy sites. Beyond fonts, the Next.js image component can also be used to optimize your site.
Start by creating a new Next.js project:
npx create-next-app@latest next-js-font-example
To best follow along with the examples, you'll want a JavaScript project with App Router.
The simplest way to add Google fonts to a Next.js project is by using next/font/google
, which automatically optimizes and self-hosts the fonts for better performance and privacy.
Modify app/layout.js
to import your Google font, and apply it globally using inter.className
on <html>
. You can add one font or multiple fonts this way:
Add some text to your homepage to test that the font has been added:
Start your app with the npm run dev
command and open your browser at localhost:3000
. You should see the text "My app". Inspecting this element will show that the computed style is "Inter".
A variable font comes in a single file that contains multiple weights and styles, whereas static fonts have a separate file for each font weight and style. Inter is typically a variable font, so let's take a look at how to work with static fonts in Next.js. An example of a common static font is Roboto.
To use static fonts in Next.js, you need to provide details on the weight and style you wish to use, otherwise only the defaults will be used.
Reloading your app should now show the Roboto font when you inspect the text:
The easiest way to add custom fonts to your Next.js project is by using next/font/local
, which allows you to self-host fonts efficiently.
WOFF2 font files are much more compressed than TTF (an older format designed for desktop usage), and are optimized for the web — so they have better performance. One way to ensure you have WOFF2 files is to go to Google Fonts and download a font (e.g., Roboto). Extract the .zip
file to find TTF font files. Then, convert the TTF files to WOFF2 format using Cloud Convert.
Place your local font files inside public/fonts
. In our example we are using Roboto locally. You can add multiple font files here:
next/font/local
in Next.jsKeep the same code for your app/page.j
s file as the Google font example above, and run your application again. Inspect the text in the browser to see that the Roboto font is being used.
Tailwind CSS is a utility-first CSS framework that provides predefined utility classes for styling elements directly in HTML.
Tailwind's font utility classes like font-sans
and font-mono
use default system fonts, so to add your own font families, you need to configure them in tailwind.config.js
. You can't add them with className={inter.className}
like you did earlier, as this creates a dynamically generated font family, which is not available to tailwind.config.js
. To use a font family in Tailwind, you need to define a CSS variable inside your font function and reference it in your tailwind.config.js
.
Before you begin, it's worth creating a new Next.js project, this time specifying that it should be set up with Tailwind CSS:
In your layout.js
file, create variables inside your font definitions:
Use the font family variables in your config file, in the fontFamily
section.
In app/global.css
, apply the font-sans
and font-mono
font families to different elements:
Then use those elements on your home page.
Running the application in the browser shows the two different Next.js fonts appearing using Tailwind CSS. Note that the fonts for the h1 and p elements are the same size — this is because @tailwind
base resets browser defaults, including the larger default size for headings.
Use global fonts: Applying fonts on a global level rather than to individual pages maintains consistency throughout your application and eliminates redundant requests, improving performance.
Use Tailwind CSS: Using Next.js fonts with Tailwind CSS simplifies styling and managing the fonts across the project. Thanks to utility classes in Tailwind, it's very easy to manage typography.
Avoid inlining fonts in next.config.js: Next.js automatically optimizes fonts, so there's no need to configure them in next.config.js
— it will only cause duplication during your build.
Subset your fonts: Load just the parts of the font you need to make your page load faster. Below, you can see the Roboto font being loaded with just the Latin characters as a subset. Also, the weight:400
property ensures that only fonts of that particular weight are loaded.
Even with Next.js optimizations, fonts can sometimes cause loading issues, rendering problems, or layout shifts.
Missing font weights or styles: You need to ensure that you're not referring to any font weights or styles that you forgot to include, otherwise the browser will display a fallback font, and displaying mixed fonts for different text sizes doesn't look very good.
Font is not supported by all browsers: To ensure cross-browser compatibility, you should use modern formats for a better chance at compatibility. It's also worth defining your own fallback fonts rather than going with the defaults, so you can control how things look across all browsers.
Duplicate font instances cause large bundle size: This happens when the same font is imported multiple times in different places in your app. If you find yourself reusing the same font many times, consider defining your fonts in a shared utility file so they can be shared across your app.
Font loading and rendering problems: These could happen because of an incorrect font import or an incorrect font path.
An example of an incorrect font import:
import { Robto } from 'next/font/google'; // ❌ Typo in "Roboto"
An example of an incorrect font path:
Content editors and other non-developer colleagues often want control over their fonts, which can be annoying for developers to manage. You can now outsource this management to Contentful.
Contentful is a headless CMS. It allows you to create flexible content models to manage your application data and deliver it via API to any front end.
An example of how to do this is as follows: If your site contains blog posts, you might already have a content model in Contentful called BlogPost
. If you create another content model called Font
with a field called name, you can then edit the BlogPost
content model so that each BlogPost
has a Font
. Create content entries for each font that you want to make available to your colleagues.
Your marketing colleagues can now create content entries for each blog post and specify the font they want. You would need to fetch the Contentful blog post data in your Next.js code, find the linked font, and dynamically set the font based on this. For more information on setting up a Next.js blog with Contentful, follow the linked tutorial.
Using Next.js fonts is a great way to optimize the fonts across your application, improving UX and SEO. Allow your marketing colleagues to retain the control they need using Contentful. Sign up to Contentful today.
Subscribe for updates
Build better digital experiences with Contentful updates direct to your inbox.