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
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:
1import { Roboto } from "next/font/google";
2
3const myFont = Roboto({
4 weight: "400",
5});
Alternatively, you can specify multiple weights by providing an array.
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.
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.
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.
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
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.
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
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
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:
1:root {
2 --primary-color: skyblue;
3}
These variables can then be accessed using the var()
function:
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.
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
.
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.
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:
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:
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.