Optimizing fonts in Next.js is a practical concern that can significantly impact your web app’s performance, user experience, and SEO. Fonts are often overlooked as a performance bottleneck, but they can cause layout shifts, slow page loads, and increased data usage if not handled properly. Over my years working with React and Next.js in production, I’ve seen how thoughtful font optimization can shave seconds off load times and improve Core Web Vitals scores.
Let me walk you through the core concepts of font optimization in Next.js, share real-world examples, common pitfalls, and best practices that I’ve picked up along the way.
Fonts contribute to the critical rendering path. When a browser encounters a font it needs to download, it can block rendering until the font is available, causing a flash of invisible text (FOIT) or a flash of unstyled text (FOUT). On top of that, large font files increase page weight, slowing down load times, especially on mobile or slow networks.
Next.js apps, being server-rendered or statically generated, have a unique opportunity to optimize fonts by controlling how and when fonts load, reducing layout shifts, and improving perceived performance.
Starting with Next.js 13, there's a built-in next/font module that simplifies font optimization. It supports Google Fonts and local fonts with automatic font subsetting, preloading, and CSS injection.
This approach is great because it:
Here’s a simple example using Google Fonts with next/font/google:
import { Roboto } from 'next/font/google';
const roboto = Roboto({
weight: ['400', '700'],
subsets: ['latin'],
display: 'swap',
});
export default function Home() {
return (
<main className={roboto.className}>
<h1>Hello, Next.js!</h1>
</main>
);
}
Notice the display: 'swap' option — this tells the browser to use a fallback font immediately and swap to Roboto once it’s loaded, preventing invisible text.
While Google Fonts CDN is convenient, self-hosting fonts can improve performance and privacy. Hosting fonts on your own CDN or server lets you control caching headers, reduce DNS lookups, and avoid third-party requests.
In Next.js, you can place font files in the public directory and load them via CSS or @font-face. For example:
@font-face {
font-family: 'MyCustomFont';
src: url('/fonts/my-custom-font.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
Then, in your global CSS or component styles, you apply font-family: 'MyCustomFont'. This approach requires you to generate subsets and variable fonts yourself, which can be done with tools like Google Webfonts Helper or Glyphhanger.
Fonts can block rendering, so preloading critical fonts is essential. Next.js’s built-in font optimization handles this automatically, but if you self-host fonts, you should add preload tags manually in your _document.js or _app.js:
<link
rel="preload"
href="/fonts/my-custom-font.woff2"
as="font"
type="font/woff2"
crossorigin="anonymous"
/>
Preloading tells the browser to fetch the font early, reducing the chance of FOIT.
font-display properly: Omitting font-display: swap or optional can cause invisible text or layout shifts.Fonts can be surprisingly heavy. A single font file can be 50-200KB or more, and loading multiple weights/styles adds up quickly. This impacts:
To optimize:
font-display: swap or optional to avoid invisible text.Fonts themselves don’t usually pose direct security risks, but loading fonts from third-party CDNs can introduce privacy concerns or availability issues. Self-hosting fonts reduces reliance on external services and can improve security by avoiding mixed content warnings or man-in-the-middle attacks on font files.
Also, when self-hosting, make sure to serve fonts with proper Access-Control-Allow-Origin headers if your app is served from multiple domains or subdomains.
In an e-commerce app, brand identity is critical, so custom fonts are often used. Here, I usually self-host the fonts to ensure consistent delivery and control caching. I subset the fonts to only the characters used in the UI and preload the most critical weights (usually regular and bold). I also use variable fonts if the brand font supports it to reduce the number of files.
Because e-commerce sites often have many pages, I integrate font loading in the global layout component to avoid redundant downloads and leverage Next.js’s built-in font optimization to manage CSS injection.
For a blog or news site where speed is crucial and font variety is limited, I rely on next/font/google for simplicity and performance. I pick only the weights I need (usually 400 and 700), specify subsets like latin, and use display: swap. This approach reduces complexity and ensures fonts are loaded efficiently without manual setup.
| Method | Pros | Cons | Best For |
|---|---|---|---|
| next/font (Google Fonts) | Automatic subsetting, preloading, easy setup, built-in CSS injection | Limited to supported fonts, less control over font files | Quick projects, Google Fonts usage, small to medium apps |
| Self-hosted fonts | Full control, better privacy, customizable caching | Manual subsetting and preloading, more setup effort | Brand fonts, privacy-focused apps, large-scale projects |
| Third-party CDN fonts (e.g., Google Fonts via link) | Easy to implement, no hosting costs | Potential privacy issues, slower if CDN is down, less control | Prototypes, small projects, quick demos |
next/font and how it simplifies font management.Optimizing fonts in Next.js is about balancing performance, user experience, and maintainability. The built-in next/font module is a great starting point for most projects, offering automatic subsetting and preloading. For apps with custom or brand fonts, self-hosting with manual subsetting and preload tags is often necessary.
Watch out for common mistakes like loading too many font weights or neglecting font-display. Always measure your font impact using tools like Lighthouse and adjust accordingly. With these strategies, you can deliver fast, visually consistent Next.js apps that keep users engaged and happy.