Payload Plugins
Pluginspayload-fonts

Reference

Plugin options, environment variables, the download CLI, the export endpoint, and every export for payload-fonts.

For AI / LLMs: View Markdown

Plugin options

fontsPlugin(options?) is the single entry point; one call registers the three collections, the fontSet global, and the export endpoint. It's zero-config. The collections and global are covered on the Collections page.

Everything the plugin registers is one key under collections (Payload collections) or globals (fontSet is a Payload global, so it lives there, not under collections) — mirroring Payload's own config shape. The plugin's own knobs — charset, families — live in a separate options bucket, kept apart from the collections and globals it registers.

Each collections.<name> / globals.fontSet entry is the same uniform shape: slug renames it (see Renaming below), overrides is a plain Partial<CollectionConfig> (or Partial<GlobalConfig>) merged onto the plugin's config, and options would carry that entry's own plugin knobs — the fonts collections and global have none. globals.fontSet also takes false to skip it.

Pass options to customize. Reference is the interactive view; TypeScript is the same shape in code, every option at its default.

enabledbooleandefault true

When false, the plugin is a no-op and registers nothing.

collections{ font?, fontOriginal?, fontOptimized? }

The collections the plugin registers, one key each. All three are always registered — there is no false, and none takes plugin-specific options.

fontCollectionOption

The visible font typeface collection. No plugin options.

slugstringdefault 'font'

Rename it; the plugin follows it (see Renaming).

overridesPartial<CollectionConfig>

Payload config merged onto the plugin's by the shared merge rules, e.g. tighter access. Rename with the sibling slug key, not here.

fontOriginalCollectionOption

The hidden fontOriginal upload collection. No plugin options.

slugstringdefault 'fontOriginal'

Rename it; the plugin follows it.

overridesPartial<CollectionConfig>

Payload config merged onto the plugin's, e.g. an upload.staticDir or a client-uploads (direct-to-Blob) adapter. Rename with the sibling slug key, not here.

fontOptimizedCollectionOption

The hidden fontOptimized upload collection (the served WOFF2s). No plugin options.

slugstringdefault 'fontOptimized'

Rename it; the plugin follows it.

overridesPartial<CollectionConfig>

Payload config merged onto the plugin's, e.g. an upload.staticDir. Rename with the sibling slug key, not here.

globals{ fontSet? }

The globals the plugin registers.

fontSetfalse | GlobalOption

The fontSet global: the active per-family selection the export endpoint and your frontend read. Registered by default; false skips it entirely — set that only if you drive the selection some other way (without it the export endpoint has nothing to resolve). No plugin options.

slugstringdefault 'fontSet'

Rename the global; the export endpoint follows it.

overridesPartial<GlobalConfig>

Payload GlobalConfig merged onto the plugin's by the shared merge rules. Rename with the sibling slug key, not here.

optionsFontsOptions

The plugin's own knobs.

charset'latin' | 'latin-ext' | stringdefault 'latin'

Characters the subsetter keeps in the served WOFF2 files: a preset (latin is ASCII + Latin-1 + common punctuation, latin-ext widens it), or an explicit string of characters to retain.

familiesFontFamilyConfig[]default sans / serif / mono / display

Font slots. The COMPLETE list: it replaces the four defaults wholesale (spread DEFAULT_FONT_FAMILIES to keep them). Each entry:

keystringrequired

Family id. Becomes a family option, a fontSet slot, and (capitalised) font<Key> / --font-set<Key>.

labelstringdefault capitalised key

Admin label for the family option and fontSet slot.

fallbackstringdefault generic sans stack

CSS fallback appended after the served family in the family variable.

accessFontsAccessOptions

Per-endpoint gates for the plugin's HTTP endpoints — one EndpointAccess ((req) => boolean | Promise<boolean>) per endpoint. See Gating endpoints.

exportEndpointAccessdefault PAYLOAD_SECRET bearer-token check

Gates GET /fonts/export. A machine endpoint with no user session — defaults to the PAYLOAD_SECRET bearer-token check the download CLI sends. Override only if you terminate auth upstream.

import { fontsPlugin } from '@pro-laico/payload-fonts'

// Every option at its default. This is the zero-config behaviour, written out.
fontsPlugin({
  enabled: true,
  // collections: {                        // optional, no defaults; each is { slug?, overrides? }
  //   font: { overrides: { /* … */ } },         // the visible font typeface collection
  //   fontOriginal: { overrides: { /* … */ } }, // the hidden originals upload collection
  //   fontOptimized: { overrides: { /* … */ } },// the hidden served-WOFF2 upload collection
  // },                                    // overrides is any Partial<CollectionConfig>; `slug` renames it
  // globals: {
  //   fontSet: {}, // {} registers the global as-is; { slug?, overrides? } tweaks it, false skips it
  // },
  options: {
    charset: 'latin',
    // families: [{ key: 'sans' }, { key: 'serif' }, { key: 'mono' }, { key: 'display' }],
    //   ^ the default, written out. Pass your own COMPLETE list to replace it
    //     (spread DEFAULT_FONT_FAMILIES to keep these and add more).
    // access: {
    //   export: () => true, // default: PAYLOAD_SECRET bearer-token check — override only if terminated upstream
    // },
  },
})

Overrides merge non-destructively, by the same rules in every plugin here:

KeyMerge
slugRenames the collection or global. Every internal reference follows it.
fieldsAppended after the plugin's. A duplicate name is a boot error naming the field.
hooksMerged per phase — yours run after the plugin's.
access / admin / upload / customShallow-merged.
defaultPopulate / forceSelectMerged as selects.
everything elseReplaced.

So collections: { fontOriginal: { overrides: { upload: { staticDir } } } } keeps the built-in font-mime whitelist, and adding a field via collections: { font: { overrides: { fields } } } keeps title / family / variable / weights.

Renaming

slug is an ordinary override. Rename anything the plugin registers and every internal reference is rebuilt around the new name — the fontOptimizedfont relationship, the fontSet slots, the upload relationships, the cleanup and optimize hooks' queries, and the slugs the export endpoint and payload fonts:download read:

fontsPlugin({
  collections: { font: { slug: 'typefaces' } },
  globals: { fontSet: { slug: 'typography' } },
})
// → editors get a `typefaces` collection and a `typography` global; nothing else to update.

Renaming is a schema change, not a label change: on an existing database it's a migration, so do it before you have font data (or migrate the tables/collections yourself).

Environment variables

Both commands write files, so both read the output vars. Only the transport differs: payload fonts:download gets its database connection from your Payload config, so the FONT_DOWNLOAD_URL / endpoint pair is dead weight there.

Env varDefaultApplies toWhat
FONT_DOWNLOAD_URLHTTP CLIRequired by payload-fonts-download. URL of the running Payload instance to fetch from. payload fonts:download doesn't use it.
PAYLOAD_SECRETbothRequired. The HTTP CLI sends it as the bearer token the export endpoint checks; payload fonts:download just needs it because it boots your Payload config. It's already in your project either way.
PAYLOAD_FONTS_ENDPOINT/api/fonts/exportHTTP CLIExport endpoint path, resolved against the site URL.
PAYLOAD_FONTS_OUTPUT_DIR./public/fontsbothWhere the downloaded WOFF2 files are written.
PAYLOAD_FONTS_DEFINITION_FILE./src/app/definition.tsbothThe generated next/font/local module.
PAYLOAD_FONTS_SRC_PREFIX../../public/fontsbothsrc path in the generated localFont() calls, relative to the definition file.
PAYLOAD_FONTS_CSS_VAR_PREFIX--font-setbothPrefix for the emitted CSS family variables; must match <PreviewFonts cssVarPrefix>.
PAYLOAD_FONTS_ENV_FILE./.env.local, ./.envbothDotenv file(s) loaded before anything else is read. Unset, both are loaded; set, only the named file.
PAYLOAD_FONTS_VERBOSEfalsebothPrint the full error on failure (also the --verbose / -v flag).

CLI commands

CommandWhat
payload fonts:downloadThe recommended build step — put it on both predev and prebuild. The baked path is the same in dev and production, so keeping definition.ts current on every boot is exactly what you want. Resolves the fontSet selection through the Local API, so no running site, no FONT_DOWNLOAD_URL, no HTTP, and writes public/fonts/*.woff2 + src/app/definition.ts. See Serving.
payload-fonts-downloadThe same job over HTTP, for a build that can't reach the database (a remote build box). Fetches GET /api/fonts/export from a running site with FONT_DOWNLOAD_URL + PAYLOAD_SECRET, and empties the definition on any failure — including when that site is unreachable, so the build can ship with no fonts. Prefer payload fonts:download where you can.

Endpoints

GET /fonts/export is gated by options.access.export, which defaults to the PAYLOAD_SECRET bearer-token check the download CLI sends — a machine endpoint with no user session. Override it only if you terminate auth upstream. See Gating endpoints.

RouteAuthWhat
GET /api/fonts/exportBearer PAYLOAD_SECRET (default; options.access.export replaces it)Returns each active family's fontOptimized bytes; the source the download CLI fetches. Response shape is ExportFontsResponse. See Serving.

Exports

ExportFromWhat
fontsPlugin@pro-laico/payload-fontsThe plugin factory (also the default export).
FontsPluginOptions@pro-laico/payload-fontsThe fontsPlugin(options?) argument type: { enabled?, collections?, globals?, options? }.
FontsOptions@pro-laico/payload-fontsThe options bucket's shape: { charset?, families?, access? }.
Charset@pro-laico/payload-fontsThe options.charset type: 'latin' | 'latin-ext' or an explicit string of characters to keep.
readFontsMarker@pro-laico/payload-fontsThe typed view of config.custom.payloadFonts: { options, fontSlug, fontOriginalSlug, fontOptimizedSlug, fontSetSlug, familyKeys, exportPath }. The supported way to discover the slugs the plugin registered — they follow collections.<name>.slug / globals.fontSet.slug, and fontSetSlug is null when the global is off. Returns undefined when the plugin isn't registered.
PayloadFontsMarker@pro-laico/payload-fontsThe marker's type.
FontFamilyConfig@pro-laico/payload-fontsThe { key, label?, fallback? } shape of a families entry.
FontsAccessOptions@pro-laico/payload-fontsThe options.access gate-map shape: { export? }.
EndpointAccess@pro-laico/payload-fontsThe endpoint-gate function type (req) => boolean | Promise<boolean>; see Gating endpoints.
DEFAULT_FONT_FAMILIES@pro-laico/payload-fontsThe built-in sans/serif/mono/display families (spread to extend them).
extractFonts@pro-laico/payload-fontsCollect the generated next/font classes for the root <html> className.
getActiveFontFaces@pro-laico/payload-fontsResolve the active fontSet selection to its served files; the read behind <PreviewFonts>, for custom UIs.
buildFontFaceCss@pro-laico/payload-fontsTurn the resolved faces from getActiveFontFaces into the @font-face + --font-set* CSS <PreviewFonts> inlines; for custom serving.
ActiveTypeface / ActiveFace@pro-laico/payload-fontsThe types getActiveFontFaces returns and buildFontFaceCss accepts.
ExportFontsResponse / ExportFamilyDiagnostics@pro-laico/payload-fontsThe shape of GET /api/fonts/export (and its per-family diagnostics), for custom consumers.
PreviewFonts@pro-laico/payload-fonts/PreviewFontsThe live preview escape hatch — inlines @font-face from the live DB selection, in dev or prod.
payload-fonts-downloadbinThe CLI the production build runs to write fonts to disk.

Collections & globals

fontsPlugin registers the font / fontOriginal / fontOptimized collections and the fontSet global. Their fields are documented on Collections.

On this page