Write your own @font-face CSS

Render custom @font-face declarations and preload webfonts by querying the GraphQL API

The simplest way to use Fontdue webfonts on your site is to include each family’s CSS URL in the page <head> (see Webfonts). For finer control – in particular, preloading a webfont file so the browser starts fetching it before it encounters the CSS – you can write the @font-face declaration yourself and pull the font url from Fontdue’s GraphQL API.

This is usually worth doing for the one or two UI fonts used in your layout’s chrome, where a first-paint flash is most visible.11The example repo ships a minimal PreloadWebfonts that preloads a single WOFF2 file; the version on this page extends it to handle color fonts with multiple tech variants.

Query the webfont sources

Every font style in the API exposes a webfontSources field – a list of { url, format, tech } entries (one per format per color-tech variant Fontdue generates). Combine it with cssFamily and name, which together form the unique font-family value used in the generated CSS22Each style in a family is declared as its own font-family, named "{cssFamily} {name}". See Webfont CSS → Two selectors per style for why..

# src/queries/RootLayout.graphql
query RootLayout {
viewer {
settings {
uiFontStyle {
cssFamily
name
webfontSources {
url
format
tech
}
}
}
}
}

The example above pulls the site’s configured UI font style from settings.uiFontStyle, but the same fields are available on any FontStyle node you’re rendering. tech carries the OpenType color-tech tag for the file – null for outline-only files (the common case), otherwise "COLRv1", "COLRv0", "SVG", "sbix", or "CBDT".

Render the @font-face block and preload hint

In a client component, use React’s ReactDOM.preload to emit a <link rel="preload" as="font"> tag, then inline a <style> element with an @font-face block that references the same URL:

// src/components/PreloadWebfonts.tsx
"use client";
import ReactDOM from "react-dom";
interface FontStyleCSS {
cssFamily: string | null;
name: string | null;
webfontSources:
| ({ url: string | null; format: string | null; tech: string | null } | null)[]
| null;
}
const createFontFaceStyle = ({ cssFamily, name, webfontSources }: FontStyleCSS) => {
const sources = webfontSources?.filter((s) => s?.format === "woff2" && s.url) ?? [];
if (sources.length === 0) return "";
const srcList = sources
.map((s) => {
const tech = s!.tech ? ` tech(color-${s!.tech})` : "";
return `url(${s!.url}) format(woff2)${tech}`;
})
.join(",\n ");
return [
`@font-face {`,
` font-family: "${cssFamily} ${name}";`,
` src: ${srcList};`,
` font-weight: 400;`,
` font-style: normal;`,
`}`,
].join("\n");
};
export default function PreloadWebfonts({ style }: { style: FontStyleCSS | null }) {
if (!style) return null;
const source = style.webfontSources?.find((s) => s?.format === "woff2");
if (source?.url) {
ReactDOM.preload(source.url, { as: "font" });
}
return (
<style
type="text/css"
dangerouslySetInnerHTML={{ __html: createFontFaceStyle(style) }}
/>
);
}

The src: list collapses to a single entry for an outline font and expands to one entry per variant for a color font, each with a tech(color-…) hint33Ordering inside webfontSources is COLRv1 → COLRv0 → SVG → sbix → CBDT for color variants, then outline files last – matching the CSS preference order the browser walks. See Webfont CSS → Color fonts.. The browser walks the list and picks the first entry it can render.

Drop the component into your root layout. React will hoist the preload hint into the document’s <head>, so it doesn’t matter where in the tree you render it:

// src/app/layout.tsx
import { fetchGraphql } from "@/lib/graphql";
import { RootLayoutQuery } from "@graphql";
import PreloadWebfonts from "@/components/PreloadWebfonts";
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const { viewer } = await fetchGraphql<RootLayoutQuery>("RootLayout.graphql");
return (
<html lang="en">
<body>
<PreloadWebfonts style={viewer.settings?.uiFontStyle} />
{children}
</body>
</html>
);
}

Only WOFF2 is preloaded – every browser that supports preload hints also supports WOFF2, so there’s no reason to preload the WOFF fallback. For a color font with multiple techs, the preload points at whichever variant Fontdue ranks first (typically COLRv1); if you know your target browsers can’t render the highest-priority variant, skip the preload for that style or point it at a specific URL by hand44A <link rel="preload"> hint can only point at one URL and doesn’t understand tech(), so preloading a variant the visitor’s browser can’t use just wastes bytes..

Apply the font

Reference the declared font-family anywhere in your CSS:

body {
font-family: "Example Sans Regular", -apple-system, sans-serif;
}

If you want Fontdue’s built-in vertical metric overrides (ascent-override, descent-override, line-gap-override), copy them in from the per-style snippets on the family’s Webfonts tab in the admin. See Webfont CSS → Vertical metric overrides for what the Fontdue-hosted CSS includes and why.

1 The example repo ships a minimal PreloadWebfonts that preloads a single WOFF2 file; the version on this page extends it to handle color fonts with multiple tech variants. 
2 Each style in a family is declared as its own font-family, named "{cssFamily} {name}". See Webfont CSS → Two selectors per style for why. 
3 Ordering inside webfontSources is COLRv1 → COLRv0 → SVG → sbix → CBDT for color variants, then outline files last – matching the CSS preference order the browser walks. See Webfont CSS → Color fonts
4 A <link rel="preload"> hint can only point at one URL and doesn’t understand tech(), so preloading a variant the visitor’s browser can’t use just wastes bytes.