Next.js Font Optimization — next/font Guide

Sanjeev SharmaSanjeev Sharma
4 min read

Advertisement

Next.js Font Optimization — next/font Guide

The next/font system automatically optimizes web fonts, eliminating layout shift and improving performance through font subsetting and caching.

Why Font Optimization Matters

Unoptimized fonts cause:

  • Cumulative Layout Shift (CLS) when fonts load
  • Slow page rendering
  • Large font file downloads
  • Flash of unstyled text (FOUT)

next/font handles all of this automatically.

Google Fonts

Use Google Fonts with zero configuration:

// app/layout.tsx
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={inter.className}>
      <body>{children}</body>
    </html>
  )
}

This automatically:

  • Downloads only the necessary characters
  • Caches fonts
  • Serves fonts locally
  • Optimizes CSS

Multiple Fonts

Combine multiple Google Fonts:

// app/layout.tsx
import { Inter, Playfair_Display } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })
const playfair = Playfair_Display({ subsets: ['latin'], weight: '700' })

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={inter.className}>
      <body>
        <header className={playfair.className}>
          <h1>My Site</h1>
        </header>
        <main>{children}</main>
      </body>
    </html>
  )
}

Font Variants

Specify font weights and styles:

import { Roboto } from 'next/font/google'

const roboto = Roboto({
  subsets: ['latin'],
  weight: ['400', '700', '900'],
  style: ['normal', 'italic'],
  variable: '--font-roboto'
})

export default function Page() {
  return (
    <html className={roboto.className}>
      <body>
        <p>Normal text</p>
        <strong>Bold text</strong>
      </body>
    </html>
  )
}

CSS Variables

Use font variables in CSS:

import { Inter } from 'next/font/google'

const inter = Inter({ variable: '--font-inter' })

export default function RootLayout({ children }) {
  return (
    <html className={inter.className}>
      <head>
        <style>{`
          body {
            font-family: var(--font-inter);
          }
        `}</style>
      </head>
      <body>{children}</body>
    </html>
  )
}

In CSS files:

/* app/globals.css */
h1 {
  font-family: var(--font-inter);
  font-size: 2rem;
}

Local Fonts

Use custom font files:

// app/layout.tsx
import localFont from 'next/font/local'

const myFont = localFont({
  src: [
    {
      path: '/fonts/MyFont-Regular.woff2',
      weight: '400',
      style: 'normal',
    },
    {
      path: '/fonts/MyFont-Bold.woff2',
      weight: '700',
      style: 'normal',
    },
    {
      path: '/fonts/MyFont-Italic.woff2',
      weight: '400',
      style: 'italic',
    },
  ],
  variable: '--font-my-font'
})

export default function RootLayout({ children }) {
  return (
    <html className={myFont.className}>
      <body>{children}</body>
    </html>
  )
}

Font files should be in public/fonts/.

Font Fallback Stack

Define fallback fonts:

import { Merriweather, Georgia } from 'next/font/google'

const merriweather = Merriweather({
  subsets: ['latin'],
  fallback: ['Georgia', 'serif']
})

export default function Page() {
  return (
    <div className={merriweather.className}>
      <h1>Fallback fonts work</h1>
    </div>
  )
}

Font Loading Strategies

Control how fonts load with display:

import { Playfair_Display } from 'next/font/google'

// swap: Show fallback immediately, swap when ready (default)
const playfair = Playfair_Display({
  subsets: ['latin'],
  display: 'swap'
})

// block: Hide text until font loads
const blocked = Playfair_Display({
  subsets: ['latin'],
  display: 'block'
})

// fallback: Use fallback if font doesn't load quickly
const withFallback = Playfair_Display({
  subsets: ['latin'],
  display: 'fallback'
})

// optional: Use fallback if font doesn't load immediately
const optional = Playfair_Display({
  subsets: ['latin'],
  display: 'optional'
})

Font Subsetting

Optimize by subsetting characters:

import { Inter } from 'next/font/google'

const inter = Inter({
  subsets: ['latin'],
  // Only include characters for specific language
  unicodeRange: 'U+0025-00FF' // Latin characters only
})

Real-World Example: Typography System

// app/layout.tsx
import { Inter, Playfair_Display, Courier_Prime } from 'next/font/google'

const inter = Inter({
  subsets: ['latin'],
  variable: '--font-sans'
})

const playfair = Playfair_Display({
  subsets: ['latin'],
  weight: '700',
  variable: '--font-serif'
})

const courier = Courier_Prime({
  subsets: ['latin'],
  weight: '400',
  variable: '--font-mono'
})

export default function RootLayout({ children }) {
  return (
    <html
      lang="en"
      className={`${inter.className} ${playfair.variable} ${courier.variable}`}
    >
      <body>{children}</body>
    </html>
  )
}

CSS:

/* app/globals.css */
body {
  font-family: var(--font-sans);
}

h1, h2, h3 {
  font-family: var(--font-serif);
}

code, pre {
  font-family: var(--font-mono);
}

Performance Optimization

Monitor font performance:

// pages/performance.tsx
import { getMetrics } from 'web-vitals'

export default function PerformancePage() {
  useEffect(() => {
    getMetrics((metric) => {
      console.log('Web Vital:', metric)
    })
  }, [])

  return <div>Check console for metrics</div>
}

Common Issues

Issue: Font not showing Solution: Ensure font files exist in public/fonts/ or use valid Google Fonts names.

Issue: Layout shift with custom fonts Solution: Set display: 'swap' or display: 'fallback'.

FAQ

Q: Can I use fonts from other sources besides Google? A: Use local fonts for any font file format. Place WOFF2 or WOFF files in public/fonts/.

Q: Does next/font support variable fonts? A: Yes, Google Fonts includes variable font options. Specify weights in an array.

Q: How do I reduce the number of font requests? A: Use variable fonts and limit to 2-3 fonts maximum. Subset fonts to needed characters.


Font optimization is a simple win for improving Core Web Vitals.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro