Fonts

Next.js gives you two options when it comes with loading fonts into your app. You can either load them from Google Fonts, or load a font file from the local file system. The good news is, no matter which option you choose, Next.js will automatically optimize the font files by self-hosting them.

Even when you opt to use Google Fonts, the font files will be downloaded during the build time, and no external requests will be sent to Google when the app is in production.

Loading from Google Fonts

To load from Google Fonts, you can use the next/font/google package.

layout.jsx

jsx
1// Import your desired fonts from Google
2// For fonts with multiple words (Roboto Mono), use the underscore (Roboto_Mono)
3import { Roboto } from "next/font/google";
4
5// Additional configurations for each font
6const myFont = Roboto({
7  . . .
8});
9
10// Embed the font
11export default function Layout({ children }) {
12  return (
13    <html lang="en">
14      <body className={myFont.className}>{children}</body>
15    </html>
16  );
17}

Inside the imported functions, you may provide additional configuration options for that specific font. Most common options include:

  • weight

If you are using a variable font, which means it has a single font file that contains multiple styles and weights, this option can be omitted.

But for a non-variable font, you must explicitly specify the weight you wish to use. For example:

jsx
1import { Roboto } from "next/font/google";
2
3const myFont = Roboto({
4  weight: "400",
5});

Alternatively, you can specify multiple weights by providing an array.

jsx
1import { Roboto } from "next/font/google";
2
3const myFont = Roboto({
4  weight: ["400", "500", "600", "700"],
5});
  • style

The style option allows you to define which font style you want to use: normal, italic, or both.

jsx
1import { Roboto } from "next/font/google";
2
3const myFont = Roboto({
4  weight: ["400", "500", "600", "700"],
5  style: ["normal", "italic"],
6});
  • subsets

Certain fonts have different subsets of characters for different languages or use cases. Specifying a subset allows you to retrieve a reduced version of the font file that contains only the characters you need.

This option is required for prefetching the font files.

jsx
1import { Roboto } from "next/font/google";
2
3const myFont = Roboto({
4  weight: ["400", "500", "600", "700"],
5  style: ["normal", "italic"],
6  subsets: ["latin"],
7});
  • display

This option allows you to specify if you want the content to be displayed with a fallback font until the custom web font loads.

It is generally recommended to set display to swap, so that the text will be displayed using a fallback font without any delays, and the browser loads the specified web font in the background and seamlessly swaps it in once available.

jsx
1import { Roboto } from "next/font/google";
2
3const myFont = Roboto({
4  weight: ["400", "500", "600", "700"],
5  style: ["normal", "italic"],
6  subsets: ["latin"],
7  display: "swap",
8});

Loading multiple fonts using utility function

Next.js allows you to load multiple fonts as well. You can create an utility function that exports multiple fonts, which can then be imported and embedded as needed. For example:

libs/fonts.js

javascript
1import { Roboto, Roboto_Mono } from "next/font/google";
2
3export const sans = Roboto({
4  subsets: ["latin"],
5  display: "swap",
6});
7
8export const mono = Roboto_Mono({
9  subsets: ["latin"],
10  display: "swap",
11});

In this case, we defined and exported two fonts, sans and mono. The fonts exported from this fonts.js can then be imported into any pages or components you want.

text
1src
2├── app
3│   ├── favicon.ico
4│   ├── globals.css
5│   ├── layout.jsx            <===== sans is used here
6│   ├── page.jsx
7│   └── users
8│       └── page.jsx          <===== mono is used here
9├── components
10│   ├── mainComponent.jsx
11│   └── sidebarComponent.jsx
12└── libs
13    └── fonts.js              <===== Two fonts are defined here, sans and mono

For example, you can design a system where sans is imported into layout.jsx and used as the primary font.

layout.jsx

javascript
1import "./globals.css";
2import { sans } from "./libs/fonts";
3
4export default function RootLayout({ children }) {
5  return (
6    <html lang="en" className={sans.className}>
7      <body>{children}</body>
8    </html>
9  );
10}

However, certain pages or components may required different fonts such as mono, which can be imported as needed.

users/page.jsx

javascript
1import { mono } from "@/libs/fonts";
2
3export default async function Page() {
4  return <div className={`${mono.className} m-4 flex gap-8`}>. . .</div>;
5}

Loading multiple fonts using CSS variables

If you prefer not to create an additional utility function, you can always opt to use the CSS variables instead.

Recall that in CSS, you can define variables like this:

css
1:root {
2  --primary-color: skyblue;
3}

These variables can then be accessed using the var() function:

css
1a {
2  color: var(--primary-color);
3}
4
5button {
6  background-color: var(--primary-color);
7}

Next.js allows you to tie the imported fonts with a CSS variable by specifying a variable option.

jsx
1import { Geist, Geist_Mono } from "next/font/google";
2
3const geistSans = Geist({
4  variable: "--font-geist-sans",
5  subsets: ["latin"],
6});
7
8const geistMono = Geist_Mono({
9  variable: "--font-geist-mono",
10  subsets: ["latin"],
11});

When embedding the fonts, use geistSans.variable instead of geistSans.className.

jsx
1export default function RootLayout({ children }) {
2  return (
3    <html lang="en">
4      <body
5        className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
6        {children}
7      </body>
8    </html>
9  );
10}

And when you need to use different fonts for different elements, use TailwindCSS's font-family utility.

jsx
1export default function Home() {
2  return (
3    <div className=". . . font-[family-name:var(--font-geist-sans)]">
4      <main className=". . .">
5        <ol className=". . . font-[family-name:var(--font-geist-mono)]">
6          . . .
7        </ol>
8      </main>
9    </div>
10  );
11}

Loading fonts files locally

Lastly, if you prefer using local fonts, they can be imported with next/font/local like this:

jsx
1import localFont from "next/font/local";
2
3const font = localFont({
4  src: "./my-font.woff2",
5  display: "swap",
6});
7
8export default function Layout({ children }) {
9  return (
10    <html lang="en" className={font.className}>
11      <body>{children}</body>
12    </html>
13  );
14}

The localFont requires a src option, which points to the location of the font file.

It is generally recommended to use a variable font so that you only have one font file to imports. However, if you are not using a variable font and have multiple font files that belong to the same font family, src also accepts an array:

jsx
1const font = localFont({
2  src: [
3    {
4      path: "./Roboto-Regular.woff2",
5      weight: "400",
6      style: "normal",
7    },
8    {
9      path: "./Roboto-Italic.woff2",
10      weight: "400",
11      style: "italic",
12    },
13    {
14      path: "./Roboto-Bold.woff2",
15      weight: "700",
16      style: "normal",
17    },
18    {
19      path: "./Roboto-BoldItalic.woff2",
20      weight: "700",
21      style: "italic",
22    },
23  ],
24});

The weight and style specifies the weight and style of that particular font file.