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.

Leave a Comment