Custom Local Fonts in Next.js 13 with Tailwind CSS
Problem Statement
Integrating custom local fonts (non-Google fonts) into a Next.js 13 project with Tailwind CSS often leads to unexpected issues. Developers face challenges configuring font files correctly, applying CSS variables, and wiring fonts to Tailwind - especially when adding multiple weights/styles. Symptoms include:
- Fonts not rendering despite correct file paths
- Missing fallbacks leading to broken typography
- Tailwind classes failing to apply the custom font
Core Solution
Leverage Next.js's built-in next/font/local
package for optimized font loading. This solution handles font bundling, CSS variable injection, and automatic fallbacks. Configure the font in your layout (or _app.js
), then integrate the CSS variable with Tailwind.
Step 1: Install Font Package (Skip if using Next.js ≥13.2)
npm install @next/font # Only required for Next.js <13.2
Step 2: Store Font Files
Place font files under /public/fonts
:
public/
fonts/
Surt-Normal-Bold.woff2
Surt-Normal-Regular.woff2
Step 3: Configure Font in Layout/App
For App Directory (/src/app/layout.jsx
):
import localFont from 'next/font/local';
// Add multiple weights/styles
const surt = localFont({
src: [
{
path: '../public/fonts/Surt-Normal-Regular.woff2',
weight: '400',
style: 'normal',
},
{
path: '../public/fonts/Surt-Normal-Bold.woff2',
weight: '700',
style: 'normal',
},
{
path: '../public/fonts/Surt-Normal-Semibold-Italic.woff2',
weight: '600',
style: 'italic',
},
],
variable: '--font-surt',
});
export default function RootLayout({ children }) {
return (
<html lang="en" className={`${surt.variable} font-sans`}>
<body>{children}</body>
</html>
);
}
For Pages Directory (/pages/_app.js
):
import localFont from '@next/font/local'; // Or 'next/font/local' for ≥13.2
const surt = localFont({
src: './fonts/Surt-Normal-Regular.woff2',
variable: '--font-surt',
});
export default function MyApp({ Component, pageProps }) {
return (
<main className={`${surt.variable} font-sans`}>
<Component {...pageProps} />
</main>
);
}
Step 4: Configure Tailwind (tailwind.config.js
)
const { fontFamily } = require('tailwindcss/defaultTheme');
module.exports = {
content: [
'./src/**/*.{js,ts,jsx,tsx}', // Adjust for your structure
],
theme: {
extend: {
fontFamily: {
sans: ['var(--font-surt)', ...fontFamily.sans],
},
},
},
plugins: [],
};
Step 5: Use Fonts in Components
Apply Tailwind's font classes:
export default function About() {
return (
<>
<h1 className="font-sans font-bold">Bold Header</h1>
<p className="font-sans">Regular text</p>
<p className="font-sans font-semibold italic">Italic semibold</p>
</>
);
}
Key Explanations
font-sans
Class Requirement:
Tailwind doesn't automatically apply the font family - you explicitly needfont-sans
(or other variant) in your HTML.Multiple Font Variants:
Bundle all weights/styles in a singlelocalFont
config using an array. Each file usesweight
andstyle
keys.CSS Variable Strategy:
variable: '--font-surt'
injects the font as a CSS variable, making it globally accessible. Tailwind consumes this in the config.Automatic Fallbacks:
Next.js automatically generates font fallbacks (visible in HTMLclassName
). Manual fallbacks aren't necessary.
Common Pitfalls & Fixes
Issue | Fix |
---|---|
Font not loading | Verify file paths start from /public |
Tailwind class not applying | Add font-sans to your elements |
Multiple weights not working | Use [{path:..., weight:...}, ...] syntax |
Styles not applying | Include style: 'italic' in config |
Final Architecture
- Fonts stored in
/public/fonts
- Font configured in root layout (
layout.jsx
) or_app.js
- CSS variable passed to
<html>
or root element - Tailwind uses variable in config
- Elements apply
font-sans
+ weight classes
Upgraded versions (Next.js ≥13.2) use native next/font/local
without separate installation. This solution optimizes font loading, avoids FOUT, and maintains Tailwind's utility-first workflow.