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?
answer
- three properties, one is conditional
- one sets the family, one does not
- what a CSS rule can actually reference
- a class that declares a custom property
basics
~20 sThe 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.
solid answer
~40 sThe call returns an object exposing `className`, `style`, and — only if you passed the `variable` option — `variable`. `className` is a generated class that sets `font-family` (and the matching `font-weight`/`font-style` when you requested a single one), so putting it on `<body>` makes that the body font. `variable` is a *different* generated class: it does not set `font-family` at all, it only declares the custom property you named, for example `--font-inter`, on whatever element carries it. That indirection is what lets other CSS own the decision — you put `inter.variable` on `<html>` and then any rule, hand-written or generated by a utility framework's theme config, can say `font-family: var(--font-inter)`. Use `className` when one element should just be in that font; use `variable` when several families need to coexist and CSS decides which applies where.
code
tsx · 12 linesimport { Inter, Roboto_Mono } from 'next/font/google'
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
const mono = Roboto_Mono({ subsets: ['latin'], variable: '--font-mono' })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={`${inter.variable} ${mono.variable}`}>
<body>{children}</body>
</html>
)
}go deeper
Know that the call returns an object and that className is what you put on an element to apply the font. Recognise inter.className on <body> in a root layout as the standard one-font setup.
Explain that variable is a second generated class that only declares the custom property, so CSS decides where the family applies. Be able to wire two families and reference them from stylesheet rules.
Argue for the indirection: typography decisions belonging in CSS or a theme config rather than scattered across JSX, one exported font module as the source of truth, and the review smell of a declared variable no rule consumes.
Set the convention for the codebase — how many families the product may ship, where they are declared, how design tokens map to them, and how you keep a second ad-hoc font from being added in one feature branch.
## The shape of what the loader returns A font loader call in Next.js is replaced at build time by an object literal. It has three properties worth knowing: - **`className`** — a generated, hashed class name. The generated CSS for that class sets `font-family` to the font plus its fallback stack. If you requested exactly one weight and style, it sets those too. - **`style`** — the same information as a React inline-style object, e.g. `{ fontFamily: '...' }`. Useful when you cannot add a class, such as styling an element whose class list is controlled by a third party. - **`variable`** — present **only** when you passed a `variable` option. It is another generated class name, and the crucial thing is what its CSS rule contains: the custom property assignment, and *not* a `font-family` declaration. That last distinction is the whole question. Candidates routinely assume `inter.variable` is the string `'--font-inter'` you passed in. It is not; it is a class name you attach to an element so the custom property exists on that element and everything beneath it. ## Direct application: className The simplest wiring puts the class on the body in the root layout: ```tsx import { Inter } from 'next/font/google' const inter = Inter({ subsets: ['latin'] }) export default function RootLayout({ children }) { return ( <html lang="en"> <body className={inter.className}>{children}</body> </html> ) } ``` Everything inherits the family. This is right when there is one font, but it hard-codes the decision into JSX: if a heading should use a different family, you have to reach for that font's `className` on the heading too, and now your typography decisions live scattered across components instead of in CSS. ## Indirect application: variable With the `variable` option, the wiring inverts: ```tsx const inter = Inter({ subsets: ['latin'], variable: '--font-inter' }) const mono = Roboto_Mono({ subsets: ['latin'], variable: '--font-mono' }) // <html className={`${inter.variable} ${mono.variable}`}> ``` Both classes go on the same element. Because neither sets `font-family`, nothing visibly changes yet — you have only made two custom properties available on the root. CSS then owns the mapping: ```css body { font-family: var(--font-inter), system-ui, sans-serif; } pre, code { font-family: var(--font-mono), monospace; } ``` This is also the shape a utility-CSS framework wants: you register `var(--font-inter)` as the theme's sans family once, and every `font-sans` utility resolves to it. The React tree stops knowing anything about typography beyond "the variables are declared at the root". Note the family names you get from a generator are hashed and not stable to type by hand, which is exactly why the custom property exists — it is the stable name you are allowed to reference. ## Choosing between them Use `className` when a single element or subtree should be in a given face and nothing else needs to know: a marketing hero, a code block in one component. Use `variable` when more than one family is in play, when a CSS framework's theme should be the source of truth, or when you want to switch families with a CSS class or media query rather than by re-rendering different components. The two are not exclusive — a common pattern is `variable` on `<html>` for the design-system wiring plus `className` on `<body>` for the default body font, which is legitimate as long as you understand you are applying two different rules. ## Where the call must live One structural point that catches people: the object is produced at build time, so the loader call belongs at module scope, and sharing the same font across files means exporting that constant from a small module (often `app/fonts.ts`) and importing it. Calling the loader again in a second file with the same options is not an error, but it is a second declaration to reason about; exporting one constant keeps a single source of truth for the family and its custom-property name. ## Subsets and the option object `subsets` is not decoration. It selects which character ranges are fetched and embedded, which directly controls file size — asking for `['latin']` on a family that also ships Cyrillic and Greek avoids shipping glyphs nobody renders. For Google fonts the loader wants this decided explicitly, because it also determines which files are worth preloading. If you request a non-variable family you must also pass `weight`; for a variable font the weight range comes from the file itself and the option is unnecessary.
- Why is the font-family name in the generated CSS a hashed string rather than "Inter"?The build generates a unique family name per loader call so that two configurations of the same family — different weights, subsets, or a local override — cannot collide in the CSS cascade. Because the name is generated and unstable, you are not meant to type it by hand; the `variable` option exists precisely to give you a stable name to reference from CSS.
- What happens if you put `inter.variable` on `<html>` but never write a rule using `var(--font-inter)`?Nothing renders in Inter. The `variable` class only declares the custom property; it deliberately sets no `font-family`. The font files are still generated and may still be preloaded, so you pay the download without the benefit — a real bug worth catching in review, and one that looks like "the font isn't working" to whoever reports it.
- When would you use the returned `style` object rather than `className`?When you cannot control the element's class list — a third-party component that only accepts a style prop, or an inline style you are composing programmatically. It carries the same family declaration. Prefer `className` otherwise, since a class participates normally in the cascade while an inline style is hard to override.
saying these in an interview costs you the question
- Thinks `inter.variable` is the literal string you passed in
- Assumes the `variable` class also sets font-family
- Hardcodes the generated hashed family name in CSS
- Says className and variable are interchangeable wiring
- Calls the loader inside a component to get a per-render value