# How to Add Custom Local Fonts to a Next.js 13 Tailwind Project Using next/font

> Learn to add custom local fonts to your Next.js 13 Tailwind project using next/font. Configure paths, apply classes, and integrate with Tailwind for faster, optimized typography.

- Repository: [Vercel/next.js](https://github.com/vercel/next.js)
- Tags: how-to-guide
- Published: 2026-02-13

---

**You can add custom local fonts to a Next.js 13 Tailwind project by configuring `next/font/local` with paths to your font files in the `public` folder, applying the generated `className` to your root layout, and optionally exposing a CSS variable via the `variable` property for Tailwind integration.**

Next.js 13 introduced the built-in `next/font` API to eliminate external network requests and runtime JavaScript when loading typography. When you add custom local fonts to a Next.js 13 Tailwind project using `next/font`, the framework automatically generates optimized `@font-face` rules, injects preload hints, and exposes CSS variables for your utility classes. This implementation relies on a specialized webpack loader and manifest plugin within the [`vercel/next.js`](https://github.com/vercel/next.js/blob/main/vercel/next.js) repository to deliver a zero-runtime solution.

## How next/font/local Works Under the Hood

The local font system operates entirely at build time through three core mechanisms in the Next.js codebase:

- **Virtual CSS Generation**: The `next-font-loader` located at [`packages/next/src/build/webpack/loaders/next-font-loader/index.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/build/webpack/loaders/next-font-loader/index.ts) intercepts imports of `next/font/local`, reads the specified font files, and emits a virtual CSS module containing the `@font-face` declarations. This loader automatically handles `unicode-range` subsetting and `font-display` settings.

- **Preload Manifest Creation**: The `NextFontManifestPlugin` in [`packages/next/src/build/webpack/plugins/next-font-manifest-plugin.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/build/webpack/plugins/next-font-manifest-plugin.ts) collects all generated font assets and constructs a manifest consumed by the server. During SSR, this manifest drives the automatic injection of `<link rel="preload">` tags into the HTML `<head>`.

- **SWC Compiler Integration**: As defined in [`packages/next/src/build/swc/options.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/build/swc/options.ts), the SWC compiler (Next.js's default) is pre-configured with `fontLoaders: ['next/font/local', 'next/font/google']`, ensuring the transformation works without Babel. Additionally, [`packages/next/src/cli/next-dev.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/cli/next-dev.ts) monitors for the deprecated `@next/font` package and emits migration warnings to ensure you're using the built-in API.

## Step-by-Step Implementation

### 1. Place Font Files in the Public Directory

Store your font files (WOFF2 recommended for optimal compression) inside the `public` folder to ensure they are served as static assets:

```text
/public/fonts/Inter/
├─ Inter-Regular.woff2
└─ Inter-Bold.woff2

```

### 2. Create the Font Configuration

Create a font definition file (commonly [`app/fonts.ts`](https://github.com/vercel/next.js/blob/main/app/fonts.ts)) that imports `localFont` from `next/font/local` and defines your font family:

```typescript
// app/fonts.ts
import localFont from 'next/font/local';

export const inter = localFont({
  src: [
    {
      path: '/fonts/Inter/Inter-Regular.woff2',
      weight: '400',
      style: 'normal',
    },
    {
      path: '/fonts/Inter/Inter-Bold.woff2',
      weight: '700',
      style: 'normal',
    },
  ],
  variable: '--font-inter',
  display: 'swap',
});

```

The `src` array accepts objects with `path`, `weight`, and `style` properties, or string shortcuts for single-weight fonts. The `variable` property registers a CSS custom property (`--font-inter`) that Tailwind can reference.

### 3. Apply to the Root Layout

Import the font into your root layout and apply the generated `className` to the `<html>` element:

```tsx
// app/layout.tsx
import './globals.css';
import { inter } from '@/app/fonts';

export const metadata = {
  title: 'Next.js Local Font Demo',
  description: 'Custom fonts with next/font',
};

export default function RootLayout({ 
  children 
}: { 
  children: React.ReactNode 
}) {
  return (
    <html lang="en" className={inter.className}>
      <body>{children}</body>
    </html>
  );
}

```

The `inter.className` value contains the generated font-family CSS and triggers the preload link injection during server-side rendering.

### 4. Configure Tailwind CSS

Extend your [`tailwind.config.js`](https://github.com/vercel/next.js/blob/main/tailwind.config.js) to map the CSS variable to a utility class:

```javascript
// tailwind.config.js
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx}',
    './pages/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {
      fontFamily: {
        inter: ['var(--font-inter)'],
      },
    },
  },
  plugins: [],
};

```

Now you can apply the font using Tailwind utilities:

```tsx
// app/page.tsx
export default function Home() {
  return (
    <h1 className="font-inter text-4xl font-bold">
      Hello from a local custom font!
    </h1>
  );
}

```

### 5. Verify the Generated Output

Run `next build && next start`, then inspect the server-rendered HTML. You should observe:

```html
<link rel="preload" href="/_next/static/media/Inter-Regular.[hash].woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/_next/static/media/Inter-Bold.[hash].woff2" as="font" type="font/woff2" crossorigin>
<style data-next-font="local">
  @font-face {
    font-family: '__Inter_Fallback_123456';
    src: url(/_next/static/media/Inter-Regular.[hash].woff2) format('woff2');
    font-display: swap;
    /* additional @font-face rules */
  }
</style>

```

The fonts are now preloaded, cached, and served without any client-side JavaScript overhead.

## Complete Working Example

Here is the full implementation combining all configuration files:

**app/fonts.ts**

```typescript
import localFont from 'next/font/local';

export const inter = localFont({
  src: [
    {
      path: '/fonts/Inter/Inter-Regular.woff2',
      weight: '400',
      style: 'normal',
    },
    {
      path: '/fonts/Inter/Inter-Bold.woff2',
      weight: '700',
      style: 'normal',
    },
  ],
  variable: '--font-inter',
  display: 'swap',
});

```

**app/layout.tsx**

```tsx
import './globals.css';
import { inter } from '@/app/fonts';

export const metadata = {
  title: 'Next.js + Tailwind Font Demo',
  description: 'Using next/font/local with Tailwind CSS',
};

export default function RootLayout({ 
  children 
}: { 
  children: React.ReactNode 
}) {
  return (
    <html lang="en" className={inter.className}>
      <body>{children}</body>
    </html>
  );
}

```

**tailwind.config.js**

```javascript
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx}',
    './pages/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {
      fontFamily: {
        inter: ['var(--font-inter)'],
      },
    },
  },
  plugins: [],
};

```

## Summary

- **`next/font/local`** in [`packages/next/src/build/webpack/loaders/next-font-loader/index.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/build/webpack/loaders/next-font-loader/index.ts) generates zero-runtime `@font-face` CSS from files in your `public` folder.
- The **`NextFontManifestPlugin`** at [`packages/next/src/build/webpack/plugins/next-font-manifest-plugin.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/build/webpack/plugins/next-font-manifest-plugin.ts) automatically creates preload links for optimal Core Web Vitals.
- Define fonts with a **`src`** array specifying paths, weights, and styles, and use the **`variable`** property to expose CSS custom properties for Tailwind.
- Apply the generated **`className`** to your root layout's `<html>` tag to activate the font family and preloading.
- Extend **[`tailwind.config.js`](https://github.com/vercel/next.js/blob/main/tailwind.config.js)** with the CSS variable to enable utility classes like `font-inter`.

## Frequently Asked Questions

### Can I use next/font/local with the Pages Router?

Yes. While the examples above use the App Router (`app` directory), `next/font/local` is fully compatible with the Pages Router. Import the font configuration in [`pages/_app.tsx`](https://github.com/vercel/next.js/blob/main/pages/_app.tsx) and apply the `className` to your top-level component or a wrapper `<div>` instead of the `<html>` element.

### What font formats are supported by next/font/local?

The loader accepts any standard web font format including WOFF2, WOFF, TTF, OTF, and EOT. However, **WOFF2** is recommended for production due to superior compression and browser support. The `next-font-loader` passes the file references directly through to the generated `@font-face` rules without transformation.

### Why does next/font use CSS variables for Tailwind integration?

The **`variable`** property creates a CSS custom property (e.g., `--font-inter`) that references the generated font-family name, which includes a hash to avoid collisions. This indirection allows Tailwind's JIT compiler to reference the font via `fontFamily: ['var(--font-inter)']` without needing to know the hashed class name generated by the webpack loader at build time.

### How does next/font improve performance compared to traditional @font-face?

Unlike manual `@font-face` declarations in global CSS, `next/font` eliminates **flash of invisible text (FOIT)** and **flash of unstyled text (FOUT)** by automatically setting `font-display: swap`. The `NextFontManifestPlugin` ensures fonts are preloaded via `<link rel="preload">` tags, and because the CSS is generated at build time by the loader in [`packages/next/src/build/webpack/loaders/next-font-loader/index.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/build/webpack/loaders/next-font-loader/index.ts), zero JavaScript is shipped to the client to handle font loading.