Atomic Payload
Features

Fonts

Manage next/font local fonts (sans, serif, mono, display) from the CMS.

Upload and pick the active sans, serif, mono, and display fonts from the admin, then ship them to disk so Next.js renders them with next/font/local.

Overview

Fonts lets admins manage the site's typefaces from the CMS instead of hard-coding them in the codebase. Font files (woff/woff2/ttf/otf) are uploaded into a Font collection, and the active sans, serif, mono, and display roles are chosen in the admin, either from the active design set (when paired with Styles) or from a standalone fontSet global. A build-time download step then turns those CMS selections into real local fonts, so the rendered site gets every next/font/local performance benefit (subset preloading, layout-shift prevention).

How it works

next/font only loads fonts from the filesystem, so it can't fetch them from object storage at request time. This feature bridges that gap with three pieces:

  • The Font collection: an upload collection where admins add font files (woff/woff2/ttf/otf). Where the font files live is Payload's job: the collection's upload.staticDir (local disk) or whatever storage plugin (@payloadcms/storage-*) is configured at the config level.
  • The active selection: which font fills each role (sans/serif/mono/display) is resolved in order: the active designSet's font group first, then the standalone fontSet global. So a Styles project and a standalone fonts project both work the same way.
  • The export endpoint and download step: the plugin registers a GET /api/fonts/export endpoint (secured by your PAYLOAD_SECRET) that resolves the active selection and reads each font's bytes server-side, either straight from disk for local storage or by fetching the url Payload reports for cloud adapters (Vercel Blob, S3, and so on). A build-time script (generate:fonts) calls that endpoint at FONT_DOWNLOAD_URL and writes the files to disk. Because the bytes are read inside Payload, there's no per-file storage token or login to manage.

The download delivers the fonts in two outputs: the font files land in public/fonts, and a generated definition module (./src/app/definition.ts by default, overridable via ATOMIC_FONTS_DEFINITION_FILE) declares each localFont(...) call (along with CSS custom properties per slot) that your app imports to wire the fonts into next/font/local. The download intentionally runs at build time (or during scaffolding), not at request time, because next/font's bundling expects file paths it can resolve at compile.

Using it

In the admin:

  1. Upload your font files into the Font collection.
  2. Pick the active fonts for each role (sans/serif/mono/display): in the active design set's font group when using Styles, or in the fontSet global when running standalone.

Running the download:

After selecting fonts, run the download step to pull the files to disk and regenerate the definition module. The fonts plugin's Setup wires this up as a generate:fonts script:

pnpm generate:fonts

It fetches the active fonts from a running, reachable Payload instance (calling the plugin's export endpoint at FONT_DOWNLOAD_URL and authenticating with PAYLOAD_SECRET), then writes the font files to public/fonts and the generated definition.ts that next/font/local consumes. It runs automatically on every build via prebuild, and you can re-run it any time the active selection changes.

If a download fails, it prints a short message by default and leaves your existing fonts in place. To see the full error, re-run it with --verbose (or set ATOMIC_FONTS_VERBOSE=true):

pnpm generate:fonts --verbose

generate:fonts needs a Payload instance to fetch from. In CI or on a deploy, point FONT_DOWNLOAD_URL at your site. For local development, before anything's deployed, a setup:fonts wrapper boots a temporary dev server, runs the download against it, and shuts it down. See the fonts plugin Notes.

Configuration

The download calls the plugin's export endpoint using:

VariablePurpose
FONT_DOWNLOAD_URLThe running Payload instance to fetch the fonts from (your dev server or deploy).
PAYLOAD_SECRETAuthenticates the request to the export endpoint (sent as Authorization: Bearer).

No storage token or script user is required: the endpoint reads each font's bytes server-side, from disk or the URL Payload reports.

Optional overrides let you change where outputs go, for example ATOMIC_FONTS_OUTPUT_DIR (defaults to ./public/fonts), ATOMIC_FONTS_DEFINITION_FILE (the generated localFont(...) module), and ATOMIC_FONTS_CSS_VAR_PREFIX (prefix for the emitted CSS custom properties).

For plugin options (enabled, fontOptions, includeFontSet, fontSetOptions), the exported configs, and the full environment-variable reference, see the fonts plugin.

Provided by

On this page