next/font

next/font and Self-Hosted Fonts

Calling a font loader is a build-time instruction, not a runtime API. Next.js 10,514 downloads the font files during next build, copies them into /_next/static/media/, generates the @font-face rules, and hands back a className, a style object and a variable name. No request reaches Google from the browser, which removes a third-party connection, a privacy problem and a render-blocking round trip at once.

app/layout.tsx — two Google fonts as CSS variablesTSX
import { Inter, Source_Serif_4 } from "next/font/google";
const inter = Inter({ subsets: ["latin"], display: "swap", variable: "--font-sans" });
const serif = Source_Serif_4({
  subsets: ["latin"], display: "swap", variable: "--font-serif",
});
export default function RootLayout({ children }: LayoutProps<"/">) {
  return (
    <html lang="en" className={`${inter.variable} ${serif.variable}`}>
      <body>{children}</body>
    </html>
  );
}

Build that and the emitted stylesheet contains seven @font-face rules per family — one per unicode-range subset, so a page of English text never downloads the Cyrillic or Greek glyphs — followed by the rule that does the real work:

Output of 73
@font-face{font-family:Inter;font-weight:100 900;font-display:swap;
  src:url(../media/83afe278b6a6bb3c-s.p.2bn3s6zvc0dyp.woff2)format("woff2");
  unicode-range:U+??,U+131,U+152-153,...}
@font-face{font-family:Inter Fallback;src:local(Arial);ascent-override:90.44%;
  descent-override:22.52%;line-gap-override:0.0%;size-adjust:107.12%}
--font-sans:"Inter", "Inter Fallback"

That second rule is how next/font reaches zero layout shift. display: swap paints fallback text immediately and swaps in Inter when it arrives — normally a visible reflow, because Arial's glyphs are a different width and its line box a different height. So Next.js reads Inter's own metrics and computes the size-adjust and the ascent, descent and line-gap overrides that make local Arial occupy exactly the same box, registering that as Inter Fallback. Source Serif 4 got local(Times New Roman) with size-adjust:117.91%; adjustFontFallback turns the behavior off or picks a different base face.

Why the metric-override fallback removes the reflow
Why the metric-override fallback removes the reflow

Preloading is scoped to where you call the loader: a font used in a page preloads on that route only, one used in a layout on every route it wraps. Within a family, only the subsets you listed are preloaded — of the fourteen woff2 files this build produced, the served HTML preloaded exactly two, the latin file of each family.

For a font you ship yourself, next/font/local takes src — one path, or an array of {path, weight, style} objects for a static family — and returns the same object. Prefer a variable font: one file covers every weight, and weight becomes optional.