Skip to content

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)

bash
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):

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):

jsx
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)

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:

jsx
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

  1. font-sans Class Requirement:
    Tailwind doesn't automatically apply the font family - you explicitly need font-sans (or other variant) in your HTML.

  2. Multiple Font Variants:
    Bundle all weights/styles in a single localFont config using an array. Each file uses weight and style keys.

  3. CSS Variable Strategy:
    variable: '--font-surt' injects the font as a CSS variable, making it globally accessible. Tailwind consumes this in the config.

  4. Automatic Fallbacks:
    Next.js automatically generates font fallbacks (visible in HTML className). Manual fallbacks aren't necessary.

Common Pitfalls & Fixes

IssueFix
Font not loadingVerify file paths start from /public
Tailwind class not applyingAdd font-sans to your elements
Multiple weights not workingUse [{path:..., weight:...}, ...] syntax
Styles not applyingInclude style: 'italic' in config

Final Architecture

  1. Fonts stored in /public/fonts
  2. Font configured in root layout (layout.jsx) or _app.js
  3. CSS variable passed to <html> or root element
  4. Tailwind uses variable in config
  5. 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.