skip to content

next/font

next/font downloads and self-hosts fonts at build time, so there's no third-party request and no flash of unstyled text at runtime. Interviewers ask about it as the concrete example of a framework removing a classic render-blocking mistake.

part ofNext.jsoverview, primer and where to startread it →
on this pageshow

explore

questions

5

In a Next.js app, what does importing a font from `next/font/google` do that a `<link>` to fonts.googleapis.com does not — where does the browser actually fetch the font file from at runtime?

level: juniorimportance: must knowfreq 70%

answer

  1. count the origins the browser touches
  2. the work happens before the request
  3. files end up in your own output
  4. one same-origin request, no gstatic hop

basics

~20 s

next/font/google downloads the font files at build time and serves them from your own origin, so the browser never contacts Google. The build generates the @font-face CSS, and the extra DNS, TLS and stylesheet round trips disappear.

solid answer

~50 s

With a `<link>` to fonts.googleapis.com the browser resolves and connects to a second origin, downloads a render-blocking stylesheet, parses it, discovers a `fonts.gstatic.com` URL, connects to a third origin, and only then fetches the woff2 — a serial chain of round trips before text can paint in the real face. `next/font/google` moves all of that to build time: the loader fetches the font binaries during `next build`, emits them into the app's own static output, and generates the `@font-face` rules pointing at those local files. At runtime there is one same-origin font request, discovered from CSS the build already shipped. Two side benefits worth naming: no third-party request means no runtime dependency on Google's availability and no user data sent to a third party, and a wrong font name or weight fails the build instead of silently falling back in production.

code

typescript · 5 lines
typescript
import { Inter } from 'next/font/google'

// Evaluated by the Next.js build: the woff2 files are fetched and
// emitted into the app's own static output, and @font-face CSS is generated.
export const inter = Inter({ subsets: ['latin'] })

go deeper

for a junior

Be able to say plainly that the font files are downloaded during the build and served from your own site, and that no request goes to Google when a user loads the page.

for a middle

Explain the round-trip chain you remove: stylesheet origin, then font origin, each with its own DNS and TLS, discovered serially. Mention that the loader call is a build-time transform, not runtime code.

for a senior

Show you can verify the claim — network panel filtered to fonts, plus grepping the shipped HTML for the Google hosts — and weigh the real tradeoffs: build-time network access, pinned font versions, and privacy or compliance drivers for self-hosting.

for a principal

Own the policy: whether every font in the product goes through next/font, how licensed faces and icon fonts are handled, and how you stop teams reintroducing third-party font links through marketing snippets or CMS-authored HTML.

## The problem next/font solves The classic way to use a Google font is a stylesheet link in the document head: ```html <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter&display=swap"> ``` That one line hides a long serial chain. The browser does a DNS lookup and TLS handshake for `fonts.googleapis.com`, downloads the stylesheet — which is render-blocking, because it is a stylesheet in the head — parses it, and only *then* discovers that the actual font binary lives on a different host, `fonts.gstatic.com`. That means a second DNS lookup and TLS handshake, and finally the woff2 download. Nothing can be parallelised, because each step's URL is only known after the previous step finishes. On a slow connection this is easily several hundred milliseconds during which the page cannot paint text in its intended typeface. ## What next/font/google does instead In Next.js you write the font as an import and a call at the top of a module: ```ts import { Inter } from 'next/font/google' const inter = Inter({ subsets: ['latin'] }) ``` This is not a runtime API. `next/font` is implemented as a build-time transform: when the compiler sees this call it fetches the font files for the requested family, weights, styles and subsets, writes them into the application's own static output, and generates `@font-face` CSS that points at those local URLs. The call expression is replaced with an object describing the generated CSS. So in the shipped application: - There is **no** `<link>` to `fonts.googleapis.com` and **no** request to `fonts.gstatic.com`. - The `@font-face` declarations arrive with the page's own CSS, which the build already knows about, rather than being discovered after a third-party stylesheet parses. - The woff2 file is served from the same origin as the app, so it reuses the connection the browser already has open. The practical effect is that the discovery chain collapses from *three origins, three round-trip groups* to *one origin, one request*, and the font request can start much earlier because the browser learns about it from first-party CSS (and, where preloading applies, from a preload hint in the initial HTML). ## What it does not do Be precise here, because interviewers probe it. Self-hosting does **not** mean there is no font download — the woff2 still has to come over the network the first time. It means the download is same-origin and discovered earlier. It also does not, by itself, mean the text is painted in the web font immediately; the default `font-display` behaviour still shows a fallback face first and swaps when the font arrives. What next/font adds on top is automatic fallback-metric adjustment so that swap does not move the layout. Another nuance candidates miss: shared cross-site font caching stopped being a benefit years ago. Browsers partition the HTTP cache by top-level site, so a visitor who downloaded Inter from Google on another site does not get a cache hit on yours. The old argument for using Google's CDN — "the user probably already has it cached" — is simply false in current browsers, which removes the main reason anyone tolerated the third-party chain. ## The build-time consequences Because the work happens at build time, the font choice is fixed at build time. That has good and awkward consequences. Good: a typo in a family name, or a weight that family does not publish, fails the build with a clear error rather than producing a silent fallback in production. Also good: your app has no runtime dependency on a third-party host, and no request carrying the user's IP and User-Agent leaves for Google on every page view — which is exactly the argument used in the GDPR discussions that pushed many teams to self-host in the first place. Awkward: the fonts are pinned to whatever the build fetched, and the build needs network access to Google's font endpoints (or a warm cache) to succeed. If a build environment is fully offline, the equivalent is `next/font/local`, where you commit the font files into the repo and point the loader at them with a `src` path. `next/font/local` gives the same generated `@font-face` output and the same self-hosted serving; it just skips the download step because you supplied the binaries. ## How to verify it in an interview answer A convincing answer names the observable check: open DevTools, filter the network panel by Font, and confirm the woff2 request is on your own origin, then search the shipped HTML for `fonts.googleapis.com` and find nothing. That is a concrete, falsifiable claim, and it is exactly what a reviewer would do to confirm someone actually migrated a page off the third-party link.

  • Someone argues the Google CDN is faster because the user probably has the font cached from another site. Is that still true?
    No. Modern browsers partition the HTTP cache by top-level site, so a font downloaded on example.com is not reused on yours — the cross-site cache-hit argument has been dead for several years. What remains of the CDN case is edge proximity, which rarely beats removing two origin handshakes and a render-blocking stylesheet from the critical path.
  • If the font binaries are not on Google at all — a licensed corporate typeface — what changes?
    You use `next/font/local` instead: commit the woff2 files into the repo and pass their paths through the loader's `src` option. The output is the same self-hosted `@font-face` CSS with the same class-name and CSS-variable surface; only the acquisition step differs, since the build has the files already rather than downloading them.
  • What happens at build time if you ask for a weight the family does not publish?
    The build fails with an error naming the family and the unavailable weight. That is intentional: because the fetch happens at build time, a bad font configuration is caught in CI rather than degrading silently to a fallback face in production, which is what a third-party stylesheet link would do.

saying these in an interview costs you the question

  • Claims the browser still requests fonts.gstatic.com, just faster
  • Says next/font eliminates the font download entirely
  • Believes Google's CDN gives cross-site cache hits today
  • Thinks next/font is a runtime hook that loads fonts on demand
  • Confuses self-hosting with inlining glyphs into the HTML

context

open as a page

A Next.js root layout calls `Inter({ subsets: ['latin'], variable: '--font-inter' })` from `next/font/google` and assigns the result to `inter`. What is on that returned object, and which property do you use to make the font available to arbitrary CSS rules rather than to one element?

level: middleimportance: should knowfreq 55%

basics

~20 s

The loader returns an object with className and style, plus variable when you pass the variable option. className applies the font family directly to an element; variable is a class that only defines the CSS custom property, which any CSS rule can then reference.

open as a page

In a Next.js App Router app, calling `Inter({ subsets: [chosenSubset] })` from `next/font/google` inside a component body fails the build, even though `chosenSubset` holds a valid string. Why does next/font insist the loader be called at module scope with literal arguments?

level: middleimportance: should knowfreq 42%

basics

~20 s

next/font is a compile-time transform, not a runtime function. The compiler must statically read the options to fetch and emit the right font files during the build, so it only accepts a call at module scope whose arguments are literals it can evaluate without running the code.

open as a page

A Next.js page still shows a visible text reflow shortly after load even though its typeface comes from `next/font/google`. Which layout-shift protections does next/font apply on its own, and where do they stop helping?

level: seniorimportance: should knowfreq 48%

basics

~20 s

next/font defaults font-display to swap and, for Google fonts, generates a metric-adjusted fallback face so the fallback occupies almost the same space as the real font. Shift returns when you disable that adjustment, supply a local font without usable metrics, or load a face outside next/font.

open as a page

A Next.js app declares a `next/font/google` face in its root layout, and every route's HTML contains a preload link for that font's woff2. What decides which routes preload a next/font file, and how would you stop it?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Preload hints are emitted for the routes whose module graph uses the font, and a face declared in the root layout is used by every route. Move the declaration into the modules that need it, or pass preload: false on the loader call.

open as a page