Next.js 16 Google Fonts: Global Inter & Exo 2 Setup
Use next/font/google to self-host Inter for body and Exo 2 for headings with Tailwind; zero layout shift and global…

⚡ Next.js Implementation Guides
In-depth Next.js guides covering App Router, RSC, ISR, and deployment. Get code examples, optimization checklists, and prompts to accelerate development.
I was setting up typography for a manufacturing website when I realized I needed different fonts for headings versus body text. Rather than manually applying fonts to every component, I discovered Next.js 16's built-in next/font/google module handles everything elegantly—with automatic self-hosting and zero layout shift.
Here's the exact process I used to set up Inter for body text and Exo 2 for all headings, globally.
Step 1: Import and Configure Your Fonts
Open your root layout file and import the fonts you want from next/font/google. In this case, we're using Inter for body text and Exo 2 for headings.
// File: app/layout.tsx
import type { Metadata } from "next";
import { Geist, Geist_Mono, Inter, Exo_2 } from "next/font/google";
import { Toaster } from "sonner";
import Navbar from "@/app/components/navbar";
import Footer from "@/app/components/footer";
import { navbarExample, footerExample } from "@/app/data";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
const inter = Inter({
variable: "--font-inter",
subsets: ["latin"],
});
const exo2 = Exo_2({
variable: "--font-exo-2",
subsets: ["latin"],
});
Each font is initialized with a CSS variable name (like --font-inter). This variable becomes available throughout your application. The subsets: ["latin"] option ensures only the Latin character set is loaded, optimizing performance.
Step 2: Apply Fonts to the Root Layout
Update your HTML body to include the font variables and the default body font class:
// File: app/layout.tsx (continued)
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html>
<body
className={`${geistSans.variable} ${geistMono.variable} ${inter.variable} ${exo2.variable} ${inter.className} antialiased`}
>
<Navbar data={navbarExample} />
{children}
<Footer data={footerExample} />
<Toaster />
</body>
</html>
);
}
Notice we're including all font variables (making them available via CSS) and applying inter.className to the body itself, which sets Inter as the default font for all text content.
Step 3: Update Your Tailwind Theme Configuration
Now configure Tailwind to use these fonts. Update your globals.css file to map the font variables into Tailwind's theme:
/* File: app/globals.css */
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-inter);
--font-mono: var(--font-geist-mono);
--font-heading: var(--font-exo-2);
/* ... rest of your theme ... */
}
By setting --font-sans: var(--font-inter), Tailwind will use Inter for all body text by default. The new --font-heading: var(--font-exo-2) line creates a custom font family that we'll use for headings.
Step 4: Apply Headings Font Automatically
In the same globals.css file, add a base layer rule that applies the heading font to all heading elements:
/* File: app/globals.css */
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
h1, h2, h3, h4, h5, h6 {
@apply font-heading;
}
}
This CSS rule is the magic piece. Every h1 through h6 element in your entire application will automatically use the Exo 2 font. No component modifications needed.
How This All Works Together
When your application loads, Next.js automatically:
- Optimizes the Google Fonts (removes external network requests)
- Self-hosts them from your domain
- Prevents layout shift by properly sizing fonts during load
- Applies Inter as the default body font via the
inter.classNameon your body element - Makes
--font-exo-2available as a CSS variable
Your Tailwind configuration then maps these fonts into utility classes (font-sans for body, font-heading for headings), and the base layer rule ensures every heading automatically gets the Exo 2 treatment without manual intervention in any component.
Result
You now have a complete, global font system where:
- All body text uses Inter
- All headings automatically use Exo 2
- Zero components need modification
- Fonts are self-hosted and optimized
- No external requests or layout shift
The beauty of this approach is that any component using standard semantic HTML heading tags (h1, h2, etc.) automatically gets the right font. No need to add classes or think about typography in individual components.
Let me know in the comments if you have questions, and subscribe for more practical development guides.
Thanks, Matija