Next.js Font Optimization — next/font Guide
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.
- Next.js Font Optimization — next/font Guide
- Why Font Optimization Matters
- Google Fonts
- Multiple Fonts
- Font Variants
- CSS Variables
- Local Fonts
- Font Fallback Stack
- Font Loading Strategies
- Font Subsetting
- Real-World Example: Typography System
- Performance Optimization
- Common Issues
- FAQ
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