Skip to main content
Forme includes three standard PDF font families by default: Helvetica, Times, and Courier (each with regular, bold, italic, and bold italic variants), plus Noto Sans (regular and bold) as a builtin Unicode fallback for non-Latin scripts. For any other typeface, register a TrueType (.ttf) font file. Registered fonts are automatically subsetted — only glyphs used in the document are embedded, keeping file sizes small.

Font.register()

Register fonts globally. Works like react-pdf’s Font.register().
import { Font } from '@formepdf/react';

Font.register({
  family: 'Inter',
  src: './fonts/Inter-Regular.ttf',
});

Font.register({
  family: 'Inter',
  src: './fonts/Inter-Bold.ttf',
  fontWeight: 'bold',
});

Font.register({
  family: 'Inter',
  src: './fonts/Inter-Italic.ttf',
  fontStyle: 'italic',
});
Then use the font by name in any style:
<Text style={{ fontFamily: 'Inter', fontSize: 14 }}>
  Regular text in Inter
</Text>
<Text style={{ fontFamily: 'Inter', fontSize: 14, fontWeight: 'bold' }}>
  Bold text in Inter
</Text>

Options

OptionTypeDefaultDescription
familystring(required)Font family name to reference in fontFamily style
srcstring | Uint8Array(required)Font source: file path, base64 data URI, or raw bytes
fontWeightnumber | "normal" | "bold"400Font weight. "normal" = 400, "bold" = 700.
fontStyle"normal" | "italic" | "oblique""normal"Font style variant

Font.clear()

Remove all globally registered fonts. Useful in tests.
Font.clear();

Multiple weights in one call

You can register multiple weights for the same family using the fonts array syntax:
Font.register({
  family: 'Inter',
  fonts: [
    { src: './fonts/Inter-Regular.ttf', fontWeight: 400 },
    { src: './fonts/Inter-Bold.ttf', fontWeight: 700 },
    { src: './fonts/Inter-Italic.ttf', fontWeight: 400, fontStyle: 'italic' },
  ],
});

Google Fonts

You can register fonts directly from a URL. Google Fonts .woff2 URLs work:
import { Font, Document, Page, Text } from '@formepdf/react';

Font.register({
  family: 'Inter',
  fonts: [
    { src: 'https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZ9hiA.woff2', fontWeight: 400 },
    { src: 'https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuI6fAZ9hiA.woff2', fontWeight: 700 },
  ],
});

export default function Doc(data) {
  return (
    <Document>
      <Page>
        <Text style={{ fontFamily: 'Inter', fontWeight: 700, fontSize: 24 }}>Bold heading</Text>
        <Text style={{ fontFamily: 'Inter', fontSize: 12 }}>Regular body text</Text>
      </Page>
    </Document>
  );
}
To find the direct .woff2 URL for a Google Font, open https://fonts.googleapis.com/css2?family=Inter:wght@400;700 in your browser and copy the url() value from the CSS.

Font fallback chains

Specify multiple font families separated by commas. Forme tries each family in order per character, so mixed-script text (e.g., English + Arabic) works automatically:
<Text style={{ fontFamily: 'Inter, Noto Sans' }}>
  English text and عربي in the same paragraph
</Text>
If a character isn’t covered by Inter, Forme tries Noto Sans. If no font in the chain covers the character, the builtin Noto Sans is used as a final fallback.

Document fonts prop

Register fonts per-document instead of globally:
<Document fonts={[
  { family: 'Roboto', src: './fonts/Roboto-Regular.ttf' },
  { family: 'Roboto', src: './fonts/Roboto-Bold.ttf', fontWeight: 'bold' },
  { family: 'Roboto', src: './fonts/Roboto-Italic.ttf', fontStyle: 'italic' },
]}>
  <Text style={{ fontFamily: 'Roboto' }}>Hello in Roboto</Text>
</Document>
Document fonts and global fonts are merged. If both register the same family + weight + style combination, the document font wins.

Font sources

The src option accepts three formats:
FormatExampleWhen to use
File path'./fonts/Inter.ttf'Local development, CLI dev server
Data URI'data:font/ttf;base64,AAAA...'Pre-encoded fonts, bundled assets
Uint8Arraynew Uint8Array(buffer)Fonts loaded from a database or API
File paths are resolved relative to the template file in the CLI dev server (forme dev), or relative to the working directory in renderDocument().

Standard fonts

These fonts are always available without registration:
FamilyWeightsStylesNotes
Helvetica400, 700normal, italicDefault font
Times400, 700normal, italic
Courier400, 700normal, italic
Noto Sans400, 700normalBuiltin Unicode fallback
Automatic fallback: When a character is not covered by your chosen font (e.g., Cyrillic or Greek text with Helvetica), Forme automatically falls back to Noto Sans. This means non-Latin text works out of the box without registering any fonts. If a fontFamily is not found, Forme falls back to Helvetica.

Example: Multiple weights

import { Font, Document, Page, View, Text } from '@formepdf/react';
import { renderDocument } from '@formepdf/core';

Font.register({ family: 'Inter', src: './fonts/Inter-Regular.ttf' });
Font.register({ family: 'Inter', src: './fonts/Inter-Medium.ttf', fontWeight: 500 });
Font.register({ family: 'Inter', src: './fonts/Inter-SemiBold.ttf', fontWeight: 600 });
Font.register({ family: 'Inter', src: './fonts/Inter-Bold.ttf', fontWeight: 700 });

const pdf = await renderDocument(
  <Document>
    <Page size="A4" margin={54}>
      <Text style={{ fontFamily: 'Inter', fontWeight: 400 }}>Regular (400)</Text>
      <Text style={{ fontFamily: 'Inter', fontWeight: 500 }}>Medium (500)</Text>
      <Text style={{ fontFamily: 'Inter', fontWeight: 600 }}>SemiBold (600)</Text>
      <Text style={{ fontFamily: 'Inter', fontWeight: 700 }}>Bold (700)</Text>
    </Page>
  </Document>
);

Troubleshooting

Text renders in Helvetica instead of my custom font

The fontFamily in your style must exactly match the family you passed to Font.register(). Font names are case-sensitive.
// Registration
Font.register({ family: 'Inter', src: './fonts/Inter-Regular.ttf' });

// ✅ Correct
<Text style={{ fontFamily: 'Inter' }}>Works</Text>

// ❌ Wrong — case mismatch
<Text style={{ fontFamily: 'inter' }}>Falls back to Helvetica</Text>

Bold or italic text falls back to Helvetica

You need to register each weight and style variant separately. If you register only the regular weight and use fontWeight: 700, Forme will look for a bold variant, not find one, and fall back.
// Only regular registered — bold will fall back
Font.register({ family: 'Inter', src: './fonts/Inter-Regular.ttf' });

// Fix: register the bold variant too
Font.register({ family: 'Inter', src: './fonts/Inter-Bold.ttf', fontWeight: 700 });

Non-Latin characters show as boxes or question marks

The font you’re using doesn’t cover those characters. Either:
  1. Register a font that covers them (e.g., Noto Sans for broad Unicode support)
  2. Use a font fallback chain: fontFamily: 'Inter, Noto Sans'
  3. Do nothing — Forme’s builtin Noto Sans provides automatic fallback for most scripts