Last updated: May 2026 · Maintained by Monotype Imaging Inc.
This repository shows how to deliver licensed web fonts in a React component library without redistributing font binaries inside the installable npm package — a separation required for compliance with standard Monotype web font licenses.
Web font use and distribution. Rendering text for visitors requires use of the font software in each end user's browser. That use requires distribution of the font software (the .woff2 binary) from your deployment to the browser — that authorized delivery is what a web font license covers. This pattern keeps that licensed delivery in the consuming application (via @font-face), not in the library package.
Distribution this pattern avoids. Embedding font files in a shared npm library transfers font software to every developer who installs the package — a separate form of distribution outside the scope of a typical web font license. The library references var(--font-family) only; the consumer app owns @font-face and font files. See lc-005 and lc-006.
The approach uses CSS custom properties (var(--font-family)) inside the component library so that @font-face rules and .woff2 files stay in the consuming application, where web font licensing and delivery are managed. A shared library that embeds font binaries transfers those files to every application that installs it, which falls outside a typical web font license. The pattern is demonstrated with a Vite-based consumer app (examples/consumer-app/) that serves fonts from its own origin via @font-face, as defined in the W3C CSS Fonts Module Level 4 specification.
This README is explanatory and non-binding; authoritative assertion text lives in reference-fonts-implementation. See also the published docs-webfonts-hub.
- The library (
src/) usesfont-family: var(--font-family)(seesrc/MyComponent.jsx) — no.woff2imports or font binaries in the published package. - Components never embed or re-export font files; the built
dist/output contains only JavaScript and CSS that reference variables.
- The consumer app (
examples/consumer-app/) defines@font-faceinfonts.cssand places files underpublic/fonts/(served by Vite as/fonts/...). examples/consumer-app/main.jsximports./fonts.cssbefore rendering so the browser parses@font-facefirst.
A component library that bundles font files transfers font software to every developer who installs the package via npm. Standard Monotype web font licenses authorize distribution of font software to end users' browsers from a licensed deployment — not transfer of font binaries through a package registry to unrelated developers. By using CSS variables, the library remains font-agnostic: authorized web delivery and license obligations stay with the deploying consumer application. See lc-005 and lc-006.
| Approach | Font files in package? | License boundary | CORS required | Recommended |
|---|---|---|---|---|
| CSS variables (this pattern) | No | Consumer app owns authorized web delivery | Same-origin: no; cross-origin host provider: yes | Yes |
| Bundled font imports in library | Yes — font software transferred to all npm consumers | Outside typical web font scope | Depends on setup | No |
| Monotype font delivery embed in library | No — but ties app to Monotype delivery service | Web font license / delivery terms | Handled by Monotype | Valid path — not this pattern |
| Unrelated third-party font service in library | No — but external font platform | Not authorized for Monotype font software | N/A | No |
| Peer dependency on font package | Yes — if package ships files | Same risk as bundling | Depends | No |
| Criterion | React + CSS variables (this pattern) | Next.js next/font/local |
|---|---|---|
| Font loading mechanism | @font-face in consumer CSS; resolved via var() |
Build-time optimization via next/font/local |
| Font files owned by | Consuming application (public/fonts/) |
Next.js app (public/fonts/ or path relative to layout) |
| Library bundles font files? | No — zero font data in library package | No — fonts stay in the app |
| CSS variable usage | Explicit: var(--font-family) |
Optional: next/font/local can expose a variable |
| CORS risk | Low if same-origin; headers required for authorized host provider | Low — fonts usually same-origin from the Next deployment |
| Tracking script required? | Depends on license; add to consumer index.html |
Depends on license; add via next/script or layout |
| Best for | Shared React libraries, Vite/CRA, design systems | Next.js apps wanting build-time subsetting |
| Related Monotype pattern | This repository | pattern-nextjs-webfonts |
Use this pattern when your component library is framework-agnostic or your app does not use Next.js. Use pattern-nextjs-webfonts when the app is Next.js and you want build-time font optimization.
This repository's demo uses examples/consumer-app/fonts.css with font-family: "MyFont", src: url("/fonts/MyFont.woff2"), and :root { --font-family: "MyFont", system-ui, sans-serif; }, with the file at examples/consumer-app/public/fonts/MyFont.woff2.
/* fonts.css — consumer application */
@font-face {
font-family: 'YourFont';
src: url('/fonts/YourFont.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap; /* See MDN: font-display */
}
:root {
--font-family: 'YourFont', sans-serif;
}The library consumes the variable only:
/* e.g. src/styles.css — or inline style in MyComponent.jsx */
.my-component {
font-family: var(--font-family, sans-serif);
}This repo's library:
src/MyComponent.jsxusesstyle={{ fontFamily: 'var(--font-family)' }}— equivalent to a stylesheet as long as the library never imports font binaries.
WOFF2 is supported in all modern browsers (Chrome 36+, Firefox 39+, Safari 10+, Edge 14+; see caniuse: WOFF2). For current-browser apps you typically need only .woff2.
When fonts are served from a different origin than the page, the font server must return Access-Control-Allow-Origin matching the page origin. Without it, browsers often show missing glyphs rather than a clear Console error — inspect the Network tab. Same-origin delivery (as in this repo's Vite demo) does not require CORS headers.
See MDN: CORS, MDN: @font-face, and the W3C CORS specification.
Step 1 — Install root dependencies. From the repository root, run npm ci to match CI and package-lock.json (use npm install only when intentionally updating dependencies).
Step 2 — Build the component library. Run npm run build. Inspect dist/ and confirm it contains no .woff2 or font binaries — only code and CSS referencing var(--font-family).
Step 3 — Place your licensed .woff2 in the consumer app's public folder. Copy the file to examples/consumer-app/public/fonts/YourFont.woff2. Vite serves public/ at the site root (static assets). Font paths are gitignored by default to avoid accidental redistribution.
Step 4 — Define @font-face in examples/consumer-app/fonts.css. Match url("/fonts/YourFont.woff2") to your filename. Use font-display: swap per MDN: font-display.
Step 5 — Set :root { --font-family: ... } in the same file (or global CSS) so library components resolve the licensed face.
Step 6 — Import fonts.css in the consumer entry point. This repo imports it in examples/consumer-app/main.jsx before rendering:
import "./fonts.css";
import App from "./App";Step 7 — (Conditional) Add a tracking script if your license requires it. Some licenses require a script alongside self-hosted fonts — separate from @font-face. Add it to examples/consumer-app/index.html or your framework's document head. See pc-012.
Step 8 — Install consumer dependencies and start the dev server.
cd examples/consumer-app
npm ci
npm run devOpen the URL Vite prints (typically http://localhost:5173) and confirm the library text uses your font.
Step 9 — (Conditional) Configure CORS if fonts move to an authorized host provider. If fonts are served from a different origin than the page, the host must return a scoped Access-Control-Allow-Origin: https://your-app-domain.com on font responses. See MDN: CORS and reference-fonts-implementation.
Step 10 — Verify the production build.
cd examples/consumer-app
npm ci
npm run buildConfirm dist/ contains the consumer's font assets only — not font files bundled from the library package.
This pattern implements assertions from reference-fonts-implementation:
lc-005— embedding involves transferring font data beyond the original userlc-006— using a font differs from distributing a fontpc-008— self-hosting web fonts requires a web font license; desktop licenses do not permit web deliverybd-001— self-hosted fonts integrate into CI/CD pipelines as versioned static assetspc-010— cross-origin font delivery requires CORS configuration; missing headers cause silent font blockingpc-012— some licenses require a tracking script alongside self-hosted fonts; this pattern covers delivery only — add a script to the consumer app when mandated. See the Clarification on pc-012.
No. A React component library that bundles font files transfers font software to every developer who installs the package via npm. Standard Monotype web font licenses authorize distribution of font software to end users' browsers from a licensed deployment — not transfer through a package registry. Keep font files in the consuming application and reference them from the library only through CSS custom properties. See lc-005.
In Monotype license terms, use of font software means employing it to generate content — for web fonts, rendering happens in the end user's browser after the font is loaded. Distribution of font software means transferring the font software itself (the binary), not only the visual output.
Web font delivery is distribution: serving a .woff2 from your deployment to a visitor's browser transfers font software. A web font license authorizes that distribution (typically scoped by domain and/or page views). Web font use always requires that distribution — the license exists to permit it.
The distinction relevant to this pattern is who receives the font software:
- Authorized web distribution — the consumer app delivers font files to end users under your web font license.
- Unlicensed redistribution (this pattern avoids) — the library embeds font binaries in an npm package, transferring font software to other developers who install the package.
The library must not bundle font files; the consumer app performs licensed delivery via @font-face. See lc-006.
You need a web font license for either path — not a desktop license. A desktop license usually covers local design and print workflows only, with no further distribution to web users.
Self-hosting (your infrastructure or an authorized third-party host provider under a written agreement) and Monotype font delivery service (authorized embed) are both delivery options under a web font license. They differ in who operates delivery, not in whether a web font license is required. Confirm your agreement covers your chosen delivery method, licensed domain(s), and whether a tracking script is required alongside self-hosted files. See reference-fonts-implementation and pc-008.
When a component references font-family: var(--font-family) instead of importing a font file, the library contains no font data. The browser resolves the family from whatever the consuming application defined in its own CSS alongside @font-face. Font files and license obligations stay entirely in the deploying app.
The most common cause is a missing Access-Control-Allow-Origin header when fonts are served from a different origin than the page. The failure often appears as missing glyphs, not a visible error — check the Network tab → Font → response headers. Serving fonts from the same origin as the Vite app (as in examples/consumer-app/) avoids this. See MDN: CORS and MDN: @font-face.
Some web font licenses require a JavaScript tracking script on every page that uses the licensed font, even when fonts are self-hosted. That obligation is separate from @font-face delivery. Add the script to the consumer app's HTML entry point when your license requires it. Per pc-012, Monotype does not process personal data in connection with the script but uses it to count page views against the licensed contingent.
No. Desktop licenses usually cover local design use only, not serving fonts to browsers via @font-face. A valid Monotype web font license scoped to your serving domain is required.
src/— component library; CSS variable references onlyexamples/consumer-app/— Vite consumer demo with@font-faceandpublic/fonts/
From the repository root, install dependencies and compile the library. The dist/ output should contain CSS with var(--font-family) references but no font binaries.
npm ci
npm run buildAfter npm run build, inspect dist/ to confirm no .woff2 files are present.
Switch into the consumer app, install its dependencies, and start the Vite dev server. The consumer owns font assets; the library only consumes --font-family.
cd examples/consumer-app
npm ci
npm run devOpen the URL in the terminal (typically http://localhost:5173). If the font does not appear, confirm a .woff2 exists at examples/consumer-app/public/fonts/ and that fonts.css url() matches the filename.
Before first run, place a licensed .woff2 in examples/consumer-app/public/fonts/ and update fonts.css. Files are gitignored by default.
This repository includes package-lock.json at the root and under examples/consumer-app/. Use npm ci in each directory to match CI.
To replicate CI and confirm the consumer app builds with a real font path:
cd examples/consumer-app && npm ci && npm run buildA successful build confirms fonts.css references an existing file, lockfile dependencies resolve, and Vite can emit the consumer dist/ — without bundling fonts into the library package.
Font files are gitignored by default. Supply files under a valid Monotype web font license, or force-add a subset for CI. When a subset .woff2 is present in examples/consumer-app/public/fonts/, it is subject to the Monotype limited-testing terms in LICENSE — not general web use or redistribution. See examples/consumer-app/public/fonts/placeholder.txt.
To commit a subset for CI, force-add once (required because public/fonts/ is gitignored):
git add -f examples/consumer-app/public/fonts/MyFont.woff2The -f flag is required once; afterward the file is tracked like any other source. The committed subset is licensed for limited testing per LICENSE, not production web use.
- Node.js 18+ (LTS)
- React 18+
- Vite 8+
- pattern-nextjs-webfonts — Next.js
next/font/local - pattern-saas-fonts-embedding — server-controlled font endpoints
- pattern-cicd-fonts-usage — CI/CD font scanning
- pattern-variable-fonts-usage — variable font axes via CSS
- docs-webfonts-hub — central developer entry point
Use GitHub Discussions (Q&A category) for questions about this pattern.
Sample application code in this repository is licensed under the MIT License. The subset font file in examples/consumer-app/public/fonts/ is included only as a build/CI demonstration asset and licensed for limited testing purposes only; it is not licensed for regular use on websites or redistribution. Please refer to the LICENSE file for both licenses. Canonical assertion text in reference-fonts-implementation remains subject to that repository's terms.