Skip to main content
Back to Blog
Tutorials
2 min read
November 10, 2024

How to Optimize Fonts in Next.js

Load fonts optimally in Next.js with zero layout shift. Google Fonts, custom fonts, variable fonts, and font subsetting.

Ryel Banfield

Founder & Lead Developer

Fonts are one of the biggest causes of layout shift and slow page loads. Next.js provides built-in font optimization that eliminates these issues.

Step 1: Google Fonts with next/font

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

const inter = Inter({
  subsets: ["latin"],
  display: "swap",
  variable: "--font-sans",
});

const playfair = Playfair_Display({
  subsets: ["latin"],
  display: "swap",
  variable: "--font-heading",
});

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

Step 2: Use Font Variables in Tailwind CSS v4

/* globals.css */
@import "tailwindcss";

@theme {
  --font-sans: var(--font-sans);
  --font-heading: var(--font-heading);
}

Use in your components:

<h1 class="font-heading text-4xl">Heading in Playfair Display</h1>
<p class="font-sans">Body text in Inter</p>

Step 3: Self-Hosted Custom Fonts

For custom fonts not on Google Fonts:

import localFont from "next/font/local";

const calSans = localFont({
  src: "./fonts/CalSans-SemiBold.woff2",
  display: "swap",
  variable: "--font-cal",
});

const geist = localFont({
  src: [
    {
      path: "./fonts/GeistVF.woff2",
      style: "normal",
    },
  ],
  variable: "--font-geist",
});

Step 4: Variable Fonts

Variable fonts contain multiple weights and styles in a single file. They are smaller and more flexible:

const inter = Inter({
  subsets: ["latin"],
  display: "swap",
  variable: "--font-sans",
  // Inter is a variable font, so all weights are included
  // No need to specify individual weights
});

For local variable fonts:

const myFont = localFont({
  src: "./fonts/MyFont-Variable.woff2",
  variable: "--font-my",
  display: "swap",
});

Use specific weights with font-weight or Tailwind's font-* utilities:

<p class="font-light">Light (300)</p>
<p class="font-normal">Regular (400)</p>
<p class="font-medium">Medium (500)</p>
<p class="font-bold">Bold (700)</p>

Step 5: Font Subsetting

Reduce font file size by only including the characters you need:

const inter = Inter({
  subsets: ["latin"], // Only include Latin characters
  display: "swap",
});

Available subsets vary by font. Common options: latin, latin-ext, cyrillic, greek, vietnamese.

For more aggressive subsetting with local fonts, use tools like glyphhanger or subfont:

# Install glyphhanger
npm install -g glyphhanger

# Subset a font to only include characters used on your site
glyphhanger https://yoursite.com --subset="*.woff2" --formats=woff2

Step 6: Font Display Strategy

The display property controls how fonts load:

const font = Inter({
  display: "swap", // Recommended: show fallback immediately, swap when loaded
});

Options:

  • swap — Shows fallback text immediately, swaps to web font when loaded (recommended)
  • block — Briefly invisible text, then web font
  • fallback — Short block period, then fallback if not loaded quickly
  • optional — Use web font only if already cached

Step 7: Preloading Critical Fonts

Next.js automatically preloads fonts used in the layout. For other font files:

// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <head>
        <link
          rel="preload"
          href="/fonts/MyFont.woff2"
          as="font"
          type="font/woff2"
          crossOrigin="anonymous"
        />
      </head>
      <body>{children}</body>
    </html>
  );
}

Step 8: Font Fallback Matching

Minimize layout shift by matching fallback font metrics to your web font:

const inter = Inter({
  subsets: ["latin"],
  display: "swap",
  adjustFontFallback: true, // Default in next/font
});

Next.js automatically adjusts the fallback font's size, line height, and letter spacing to match the web font.

Step 9: Icon Fonts vs SVG Icons

Avoid icon fonts. Use inline SVGs instead:

// Bad: icon font (loads entire font file)
<i className="fa fa-home" />

// Good: inline SVG (only loads what you use)
<svg className="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor">
  <path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
</svg>

Benefits of SVGs:

  • Only load the icons you actually use
  • Scalable without quality loss
  • Styleable with CSS
  • No additional HTTP requests

Performance Checklist

  • Use next/font for all fonts (Google and local)
  • Prefer variable fonts over multiple font files
  • Include only needed subsets
  • Use display: "swap" for text visibility
  • Limit the number of font families (2-3 max)
  • Use woff2 format for smallest file size
  • Avoid icon fonts, use SVGs

Need Performance Optimization?

We optimize websites for Core Web Vitals and page speed, including font loading strategies. Contact us for a performance audit.

fontsperformanceNext.jsweb performancetutorial

Ready to Start Your Project?

RCB Software builds world-class websites and applications for businesses worldwide.

Get in Touch

Related Articles