Next.js Curriculum
MediumAsset Optimization100% Free25 min read

Zero-CLS Font Optimization with next/font

The 30-Second Interview Answer

next/font automatically hosts Google Fonts and custom fonts locally at build time, eliminating external network requests to Google servers for GDPR compliance. It injects automatic fallback CSS font-adjust metrics (size-adjust, ascent-override, descent-override) that match the fallback system font geometry to the custom font, eliminating Cumulative Layout Shift (CLS).

Learning Objectives

Integrate Google and local fonts using next/font/google and next/font/local.

Explain how font metric overrides eliminate Cumulative Layout Shift (CLS).

Comply with GDPR by eliminating third-party external requests to Google font servers.

Apply font variables across Tailwind CSS and CSS modules.

Prerequisites & Background Knowledge
Web font loading lifecycle (FOIT vs FOUT)Cumulative Layout Shift (CLS)CSS variables

Why Interviewers Care

Crucial for achieving 100/100 Lighthouse performance scores and ensuring European privacy compliance.

The Challenge

How does next/font achieve zero Cumulative Layout Shift (CLS)? Implement both a Google variable font and a custom local font with Tailwind CSS integration.

Code Architecture & Implementation

1// app/layout.tsx
2import { Inter } from 'next/font/google';
3import localFont from 'next/font/local';
4import './globals.css';
5
6// 1. Google Font: Downloaded and self-hosted locally at build time!
7const inter = Inter({
8 subsets: ['latin'],
9 display: 'swap',
10 variable: '--font-inter', // CSS Variable for Tailwind
11});
12
13// 2. Local Font with multiple weights:
14const geistSans = localFont({
15 src: [
16 { path: './fonts/Geist-Regular.woff2', weight: '400', style: 'normal' },
17 { path: './fonts/Geist-Bold.woff2', weight: '700', style: 'normal' },
18 ],
19 variable: '--font-geist-sans',
20 display: 'swap',
21});
22
23export default function RootLayout({ children }: { children: React.ReactNode }) {
24 return (
25 <html lang="en" className={`${inter.variable} ${geistSans.variable}`}>
26 <body className="font-sans antialiased">{children}</body>
27 </html>
28 );
29}

Architectural Explanation

When browsers load external web fonts, they either hide text until the font loads (Flash of Invisible Text - FOIT) or show a fallback system font that swaps when the custom font arrives (Flash of Unstyled Text - FOUT). Because Arial and Inter have different letter widths and heights, swapping them pushes surrounding layout elements down, triggering CLS. next/font computes the exact glyph bounding box differences and generates a custom fallback @font-face rule using size-adjust and ascent-override. The fallback font occupies the EXACT same pixel height and width as Inter, achieving a 0.000 CLS score.

Common Mistakes & Anti-Patterns

Using `<link rel="stylesheet" href="https://fonts.googleapis.com/...">`, making external requests that violate GDPR and cause CLS.

Loading 10 distinct font weights instead of utilizing modern variable fonts.

Instantiating font objects inside component functions instead of module scope.

Interview Follow-Up Traps

The interviewer asks: "Do requests to Google Fonts via next/font occur on every client page visit?" No! Google Fonts are downloaded by the build server once during `next build` and packaged as static woff2 files served from your own domain.

Frequently Asked Questions

What is display: "swap"?

It instructs the browser to immediately render text using the adjusted fallback font, swapping to the custom font as soon as it finishes loading.

Yes, via the `subsets: ["latin"]` option to strip unused Cyrillic/Greek glyphs and save bandwidth.

Yes! You can apply `className={inter.className}` directly to any element.