Next.js Fonts
Fonts affect how your website looks and how fast it loads. Loading fonts incorrectly causes a visual glitch called layout shift — text appears in one font and then jumps to another when the custom font finally loads. Next.js has a built-in font system that eliminates this problem and makes Google Fonts easy to use without sending requests to Google's servers.
The Problem with Traditional Font Loading
When you add a Google Font link in your HTML, the browser makes a network request to Google's servers to download the font. This request adds delay. While the font downloads, the browser either shows no text (flash of invisible text) or shows a fallback font that gets replaced once the real font loads.
TRADITIONAL GOOGLE FONTS:
Page loads → Browser requests font from Google's servers
→ Waits for font to download
→ Font swaps in → Text jumps position
Result: Layout shift, slower load, privacy concerns (request to Google)
NEXT.JS FONT SYSTEM:
Build time → Font downloaded once → Self-hosted with your app
Page loads → Font already available → No network request → No shift
Result: Instant font, no layout shift, no third-party request
Using Google Fonts in Next.js
Next.js provides the next/font/google package. You import the font you want, call it as a function with your options, and apply the result to your layout or component.
// app/layout.js
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap',
});
export default function RootLayout({ children }) {
return (
<html lang="en" className={inter.className}>
<body>{children}</body>
</html>
);
}
Next.js downloads the font file during the build process and serves it from your own domain. The font loads instantly with zero layout shift.
Choosing Font Subsets
Google Fonts contain characters for many languages. The subsets option tells Next.js to download only the characters your site uses. This keeps the font file small.
const roboto = Roboto({
subsets: ['latin'], // English and most Western European languages
weight: ['400', '700'], // Regular and Bold only
});
// Other common subsets:
// 'latin-ext' → Extended Latin characters
// 'cyrillic' → Russian, Bulgarian, etc.
// 'greek' → Greek alphabet
// 'vietnamese' → Vietnamese characters
Diagram: Subset Selection
FULL FONT FILE: Latin + Latin-ext + Cyrillic + Greek + Vietnamese = 800KB WITH subsets: ['latin'] ONLY: Latin characters only = 180KB Result: 78% smaller file, faster load time Only load what your users actually read
Using Multiple Fonts
Many designs use two fonts — one for headings and one for body text. Define both fonts and apply them to different elements using CSS variables.
import { Playfair_Display, Source_Sans_3 } from 'next/font/google';
const playfair = Playfair_Display({
subsets: ['latin'],
variable: '--font-heading',
});
const sourceSans = Source_Sans_3({
subsets: ['latin'],
variable: '--font-body',
});
export default function RootLayout({ children }) {
return (
<html
lang="en"
className={`${playfair.variable} ${sourceSans.variable}`}
>
<body>{children}</body>
</html>
);
}
Then use the CSS variables in your stylesheets:
h1, h2, h3 {
font-family: var(--font-heading);
}
body, p {
font-family: var(--font-body);
}
Using Local (Custom) Fonts
If you have a custom font file — a brand font or a purchased typeface — use next/font/local to load it.
import localFont from 'next/font/local';
const brandFont = localFont({
src: './fonts/BrandFont-Regular.woff2',
variable: '--font-brand',
});
Place the font file inside your app/ folder or inside public/fonts/. The src path is relative to the file where you define the font.
Loading Multiple Weights and Styles
import localFont from 'next/font/local';
const brandFont = localFont({
src: [
{
path: './fonts/Brand-Regular.woff2',
weight: '400',
style: 'normal',
},
{
path: './fonts/Brand-Bold.woff2',
weight: '700',
style: 'normal',
},
{
path: './fonts/Brand-Italic.woff2',
weight: '400',
style: 'italic',
},
],
variable: '--font-brand',
});
The display: swap Option
The display: 'swap' option tells the browser to show a fallback font immediately while the custom font loads. Once the custom font is ready, it swaps in. This prevents invisible text on slow connections.
display: 'auto' → Browser decides (can cause invisible text) display: 'swap' → Show fallback immediately, swap when ready display: 'block' → Hide text briefly, then show with custom font display: 'fallback' → Short hide, then use fallback if font is slow display: 'optional' → Use custom font only if cached, else use fallback
Key Takeaway
Use next/font/google to add Google Fonts without external network requests. Use next/font/local for custom font files. Always specify the subsets your content needs to keep font files small. Apply fonts via className or CSS variables. Next.js handles all font optimization automatically — zero layout shift, zero configuration.
