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 fontfallback— Short block period, then fallback if not loaded quicklyoptional— 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/fontfor 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
woff2format 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.