---
title: "Font Optimization"
description: "Font Optimization (next/font) is a built-in Next.js subsystem engineered to eliminate external network requests for web fonts at runtime while automatically preventing Cumulative Layout Shift (CLS)..."
last_updated: "2026-09-23T10:52:03.135025+00:00"
canonical_url: "https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/ecosystem-packages/font-optimization"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/font/src/google/loader.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/google/loader.ts)
- [packages/next/font/google/index.js](https://github.com/blade47/next.js/blob/main/packages/next/font/google/index.js)
- [packages/next/font/local/index.js](https://github.com/blade47/next.js/blob/main/packages/next/font/local/index.js)
- [packages/font/src/google/get-fallback-font-override-metrics.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/google/get-fallback-font-override-metrics.ts)
- [packages/font/src/local/get-fallback-metrics-from-font-file.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/local/get-fallback-metrics-from-font-file.ts)
- [packages/font/google/index.js](https://github.com/blade47/next.js/blob/main/packages/font/google/index.js)
- [packages/font/src/local/loader.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/local/loader.ts)
- [packages/font/local/index.js](https://github.com/blade47/next.js/blob/main/packages/font/local/index.js)
- [packages/next/src/pages/_document.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/pages/_document.tsx)
- [packages/next/src/next-devtools/dev-overlay/font/font-styles.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/font/font-styles.tsx)
- [packages/font/src/google/fetch-css-from-google-fonts.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/google/fetch-css-from-google-fonts.ts)
- [packages/next/src/next-devtools/server/font/get-dev-overlay-font-middleware.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/server/font/get-dev-overlay-font-middleware.ts)
- [packages/next-codemod/transforms/__testfixtures__/built-in-next-font/page.output.tsx](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/__testfixtures__/built-in-next-font/page.output.tsx)
- [packages/next/font/google/target.css](https://github.com/blade47/next.js/blob/main/packages/next/font/google/target.css)
- [packages/eslint-plugin-next/src/rules/no-page-custom-font.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-page-custom-font.ts)
- [packages/font/src/google/fetch-font-file.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/google/fetch-font-file.ts)
- [packages/next-codemod/transforms/__testfixtures__/built-in-next-font/page.input.tsx](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/__testfixtures__/built-in-next-font/page.input.tsx)
- [packages/next/src/server/font-utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/font-utils.ts)
- [packages/font/src/local/pick-font-file-for-fallback-generation.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/local/pick-font-file-for-fallback-generation.ts)
- [packages/eslint-plugin-next/src/rules/google-font-display.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/google-font-display.ts)
- [packages/next/font/local/target.css](https://github.com/blade47/next.js/blob/main/packages/next/font/local/target.css)
- [packages/font/src/local/index.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/local/index.ts)
- [packages/font/src/google/validate-google-font-function-call.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/google/validate-google-font-function-call.ts)
- [packages/font/google/target.css](https://github.com/blade47/next.js/blob/main/packages/font/google/target.css)
- [packages/next/font/index.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/font/index.d.ts)
- [packages/next/font/google/index.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/font/google/index.d.ts)
- [packages/font/src/google/find-font-files-in-css.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/google/find-font-files-in-css.ts)
- [packages/next/font/local/index.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/font/local/index.d.ts)
- [packages/font/local/target.css](https://github.com/blade47/next.js/blob/main/packages/font/local/target.css)
- [packages/font/src/google/google-fonts-metadata.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/google/google-fonts-metadata.ts)
</details>

## Overview

Font Optimization (`next/font`) is a built-in Next.js subsystem engineered to eliminate external network requests for web fonts at runtime while automatically preventing Cumulative Layout Shift (CLS). By default, fetching fonts from external providers like Google Fonts or self-hosting local font files introduces privacy concerns, layout jumps during font loading, and additional DNS round-trips. The `next/font` package addresses these challenges by shifting font fetching, subset extraction, and metric calculation to build time.

Sources: [packages/font/src/google/loader.ts:28-194](https://github.com/blade47/next.js/blob/main/packages/font/src/google/loader.ts#L28-L194)

The subsystem comprises two primary loaders: `nextFontGoogleFontLoader` for Google Fonts and `nextFontLocalFontLoader` for local font files. During compilation, these loaders download or read font binary buffers, parse metadata using `fontkit` or precalculated metrics (`capsizeFontsMetrics`), emit optimized font files into `.next/static/media`, and automatically inject sizing adjustments (`ascent-override`, `descent-override`, `line-gap-override`, and `size-adjust`) to match fallback system fonts like Arial or Times New Roman. This ensures zero layout shifts while completely self-hosting web typography.

Sources: [packages/font/src/local/loader.ts:15-112](https://github.com/blade47/next.js/blob/main/packages/font/src/local/loader.ts#L15-L112)

---

## Google Fonts Loading Pipeline

The Google Fonts loader (`nextFontGoogleFontLoader`) handles validation, URL construction, remote CSS fetching, binary downloading, and local asset replacement. When invoked during compilation, it validates options against static metadata (`googleFontsMetadata`), builds a Google Fonts API request URL containing selected weights, styles, and variable axes, and downloads the resulting CSS declarations.

Sources: [packages/font/src/google/loader.ts:28-95](https://github.com/blade47/next.js/blob/main/packages/font/src/google/loader.ts#L28-L95)

```mermaid
sequenceDiagram
    participant Caller as Build / Compiler
    participant Loader as nextFontGoogleFontLoader
    participant Google as Google Fonts API
    participant Emit as emitFontFile

    Caller->>Loader: Invoke with functionName & data
    Loader->>Loader: validateGoogleFontFunctionCall()
    Loader->>Google: fetchCSSFromGoogleFonts(url)
    Google-->>Loader: Return @font-face CSS
    Loader->>Loader: findFontFilesInCss()
    loop For each font file URL
        Loader->>Google: fetchFontFile(googleFontFileUrl)
        Google-->>Loader: Font Buffer (.woff2)
        Loader->>Emit: emitFontFile(buffer, ext, preload, adjustMetrics)
        Emit-->>Loader: selfHostedFileUrl
    end
    Loader->>Loader: Replace remote URLs with selfHostedFileUrl
    Loader-->>Caller: Return CSS and font configuration
```

Sources: [packages/font/src/google/loader.ts:28-162](https://github.com/blade47/next.js/blob/main/packages/font/src/google/loader.ts#L28-L162)

The step-by-step control flow through the Google Fonts loader follows a strict sequence:
1. `validateGoogleFontFunctionCall()`: Validates that the requested font family exists, checks requested subsets, weights, and styles against `googleFontsMetadata`, and returns structured `FontOptions`.
2. `getFontAxes()` / `getGoogleFontsUrl()`: Computes the required font axes and builds the target request URL targeting modern user agents to guarantee `.woff2` responses.
3. `fetchCSSFromGoogleFonts()`: Fetches the CSS payload using a retry mechanism (`retry`) and in-memory caching (`cssCache`) to prevent duplicate compilation requests.
4. `findFontFilesInCss()`: Parses the CSS string line-by-line, tracking subset comment annotations (e.g., `/* latin */`) to determine whether individual font files match user-specified preload subsets.
5. `fetchFontFile()` & `emitFontFile()`: Downloads binary font buffers, caches them via `fontCache`, and emits them to `.next/static/media`.
6. URL Replacement: Replaces external `fonts.gstatic.com` URLs in the CSS string with the emitted local `selfHostedFileUrl` paths.

Sources: [packages/font/src/google/loader.ts:28-162](https://github.com/blade47/next.js/blob/main/packages/font/src/google/loader.ts#L28-L162), [packages/font/src/google/fetch-css-from-google-fonts.ts:12-36](https://github.com/blade47/next.js/blob/main/packages/font/src/google/fetch-css-from-google-fonts.ts#L12-L36), [packages/font/src/google/find-font-files-in-css.ts:6-38](https://github.com/blade47/next.js/blob/main/packages/font/src/google/find-font-files-in-css.ts#L6-L38)

---

## Local Font Embedding and File Resolution

The local font loader (`nextFontLocalFontLoader`) processes self-hosted font arrays specified via `next/font/local`. It resolves file paths relative to the calling module, reads binary buffers from the filesystem, extracts font metadata using `fontkit`, and dynamically constructs `@font-face` CSS declarations.

Sources: [packages/font/src/local/loader.ts:15-34](https://github.com/blade47/next.js/blob/main/packages/font/src/local/loader.ts#L15-L34)

```mermaid
flowchart TD
    A["Validate local font call"] --> B["Map over src array"]
    B --> C["Resolve path via resolve()"]
    C --> C1["fs.readFile(resolved)"]
    C1 --> D["emitFontFile() -> selfHostedFileUrl"]
    D --> E["Load metadata via fontFromBuffer()"]
    E --> F["Construct @font-face CSS properties"]
    F --> G["pickFontFileForFallbackGeneration()"]
    G --> H["getFallbackMetricsFromFontFile()"]
    H --> I["Return CSS, fallback metrics, and variables"]
```

Sources: [packages/font/src/local/loader.ts:35-112](https://github.com/blade47/next.js/blob/main/packages/font/src/local/loader.ts#L35-L112)

When multiple font files are provided in the `src` array, `pickFontFileForFallbackGeneration()` determines which font file is used to calculate automatic fallback metrics. It evaluates weight distance from normal weight (`400`), prefers normal style over italic, and breaks ties by choosing the thinner font variant.

Sources: [packages/font/src/local/pick-font-file-for-fallback-generation.ts:68-104](https://github.com/blade47/next.js/blob/main/packages/font/src/local/pick-font-file-for-fallback-generation.ts#L68-L104)

> [!NOTE]
> If `font-family` is explicitly declared in the `declarations` array of a local font call, `nextFontLocalFontLoader` respects the custom property; otherwise, it automatically assigns the generated variable name as the `font-family`.

Sources: [packages/font/src/local/loader.ts:57-67](https://github.com/blade47/next.js/blob/main/packages/font/src/local/loader.ts#L57-L67)

---

## Fallback Font Override Metrics and Size Adjustment

To prevent layout shift when web fonts swap in for fallback fonts, `next/font` calculates sizing adjustments (`size-adjust`, `ascent-override`, `descent-override`, and `line-gap-override`). For Google Fonts, `getFallbackFontOverrideMetrics()` invokes `calculateSizeAdjustValues()`, which executes the call chain `nextFontGoogleFontLoader` → `getFallbackFontOverrideMetrics` → `calculateSizeAdjustValues` → `formatName` to normalize the font family string, look up metrics in `capsizeFontsMetrics`, compute proportion factors, and format override percentages. For local fonts, `getFallbackMetricsFromFontFile()` uses `fontkit` to inspect glyph metrics and compute average character widths (`calcAverageWidth`).

Sources: [packages/font/src/google/get-fallback-font-override-metrics.ts:13-27](https://github.com/blade47/next.js/blob/main/packages/font/src/google/get-fallback-font-override-metrics.ts#L13-L27), [packages/next/src/server/font-utils.ts:6-44](https://github.com/blade47/next.js/blob/main/packages/next/src/server/font-utils.ts#L6-L44)

| Property | Description | Calculation Basis |
| :--- | :--- | :--- |
| `size-adjust` | Scales fallback font glyph dimensions to match primary font. | `mainFontAvgWidth / fallbackFontAvgWidth` |
| `ascent-override` | Overrides fallback font ascent metric. | `ascent / (unitsPerEm * sizeAdjust)` |
| `descent-override` | Overrides fallback font descent metric. | `descent / (unitsPerEm * sizeAdjust)` |
| `line-gap-override` | Overrides fallback font line gap metric. | `lineGap / (unitsPerEm * sizeAdjust)` |
| `fallbackFont` | Fallback system font name. | `Arial` (sans-serif) or `Times New Roman` (serif) |

Sources: [packages/font/src/local/get-fallback-metrics-from-font-file.ts:73-95](https://github.com/blade47/next.js/blob/main/packages/font/src/local/get-fallback-metrics-from-font-file.ts#L73-L95), [packages/next/src/server/font-utils.ts:19-44](https://github.com/blade47/next.js/blob/main/packages/next/src/server/font-utils.ts#L19-L44)

> [!WARNING]
> Character average width calculation (`calcAverageWidth`) currently evaluates a fixed sample string (`aaabcdeeeefghiijklmnnoopqrrssttuvwxyz      `) designed specifically for the Latin alphabet. Non-Latin alphabets may fail character coverage checks and skip automatic `size-adjust` calculations.

Sources: [packages/font/src/local/get-fallback-metrics-from-font-file.ts:21-52](https://github.com/blade47/next.js/blob/main/packages/font/src/local/get-fallback-metrics-from-font-file.ts#L21-L52)

---

## Design Trade-offs

| Design Choice | Benefit | Cost |
| :--- | :--- | :--- |
| **Build-time fetching & embedding** | Eliminates runtime external network requests and improves privacy. | Requires build-time network access or cached responses. |
| **Precalculated Google Font metrics** | Avoids parsing remote font binaries during loader execution. | Requires updating metric tables when Google Fonts metadata changes. |
| **In-memory CSS and file caches** | Prevents redundant duplicate fetches across client and server compilers. | Consumes heap memory during long-lived build processes. |
| **Automatic size-adjust fallbacks** | Prevents Cumulative Layout Shift (CLS) during font loading. | Limited to Latin character frequency weighting tables. |

Sources: [packages/font/src/google/loader.ts:13-92](https://github.com/blade47/next.js/blob/main/packages/font/src/google/loader.ts#L13-L92), [packages/font/src/google/get-fallback-font-override-metrics.ts:7-12](https://github.com/blade47/next.js/blob/main/packages/font/src/google/get-fallback-font-override-metrics.ts#L7-L12)

---

## ESLint Rules and Validation

Next.js provides ESLint rules in `@next/eslint-plugin-next` to enforce font optimization best practices and prevent misconfigurations.

Sources: [packages/eslint-plugin-next/src/rules/no-page-custom-font.ts:12-21](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-page-custom-font.ts#L12-L21)

- `no-page-custom-font`: Prevents adding Google Fonts via manual `<link>` tags inside custom pages or `pages/_document.js`, ensuring automatic optimization is not bypassed.
- `google-font-display`: Enforces `font-display` configuration on Google Font link tags, recommending `&display=optional` and flagging unoptimized display settings like `auto`, `block`, or `fallback`.

Sources: [packages/eslint-plugin-next/src/rules/no-page-custom-font.ts:152-167](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-page-custom-font.ts#L152-L167), [packages/eslint-plugin-next/src/rules/google-font-display.ts:39-50](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/google-font-display.ts#L39-L50)

---

## Usage Example

The following example demonstrates how to configure and use optimized Google and local fonts in a Next.js application:

```typescript
import { Inter, Oswald } from 'next/font/google'
import localFont from 'next/font/local'

// Configure Google Font with subsets and variable styling
const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-inter',
})

// Configure Local Font with custom fallback metrics
const myFont = localFont({
  src: './my-font.woff2',
  display: 'swap',
  adjustFontFallback: 'Arial',
})

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

Sources: [packages/next-codemod/transforms/__testfixtures__/built-in-next-font/page.output.tsx:3-18](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/__testfixtures__/built-in-next-font/page.output.tsx#L3-L18), [packages/font/src/local/index.ts:9-25](https://github.com/blade47/next.js/blob/main/packages/font/src/local/index.ts#L9-L25)

## Related

- [App Server Rendering](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/app-router-rendering/app-server-rendering)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/llms.txt) for all pages in this wiki.
