PDF generation with JSX. Page breaks that actually work.
Every PDF tool makes you choose: fight with CSS page breaks or use an editor that can't handle dynamic data. Forme is a layout engine built for pages. No headless browser. No Chrome. Renders in milliseconds. Runs anywhere Node runs.
npm install @formepdf/cli @formepdf/react @formepdf/coreimport { Document, Page, View, Text } from '@formepdf/react';
import { renderDocument } from '@formepdf/core';
const pdf = await renderDocument(
<Document>
<Page size="Letter" margin={36}>
<Text style={{ fontSize: 24, fontWeight: 'bold' }}>Invoice #2024-001</Text>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: 24 }}>
<Text>Widget Pro</Text>
<Text>$49.00</Text>
</View>
</Page>
</Document>
);
// pdf is a Uint8Array: save it, serve it, email itnpx forme dev invoice.tsx --data sample.jsonLive preview with debug overlays. Click any element to inspect its computed styles.
- Page-native layout: Content flows into pages, not onto an infinite canvas. Page breaks happen at the right place, every time.
- React components: Document, Page, View, Text, Image, Table. If you know React, you know Forme.
- Live preview:
forme devshows your PDF updating in real time as you edit. - Click-to-inspect: Select any element to see its box model, computed styles, and position.
- Debug overlays: Toggle bounding boxes, margins, and page break points.
- Fast: Rust engine compiled to WASM. Renders in milliseconds, not seconds.
- Flex wrap + align-content: Flex containers wrap across pages correctly.
align-contentdistributes wrapped lines (center,space-between,space-around,space-evenly,flex-end,stretch). - Widow/orphan control: Text paragraphs never leave a single orphan line at the bottom of a page or a single widow line at the top. Configurable via
minWidowLinesandminOrphanLines. - Table overflow: Table cells with content taller than a page are preserved across page breaks, not silently clipped.
- Absolute positioning:
position: 'absolute'withtop,right,bottom,leftrelative to the parent View. - Column flex:
justifyContentandalignItemswork in both row and column directions. - SVG: Inline SVG rendering with support for
rect,circle,ellipse,line,polyline,polygon, andpathelements. - Custom fonts: TrueType font embedding with automatic subsetting.
- Links: Add
hrefto any<Text>,<View>,<Image>, or<Svg>for clickable PDF links. - Bookmarks: Add
bookmarkto any element for PDF outline entries. Navigate long documents from the bookmark panel. - Inline text styling: Nest
<Text>inside<Text>to bold a word, change colors mid-sentence, or apply strikethrough. - Images: JPEG and PNG with transparency support.
alttext for accessibility. - CSS shorthands:
border: "1px solid #000",padding: "8 16",margin: [20, 40]— CSS-style shorthand strings and arrays parse automatically. - Document language:
<Document lang="en-US">sets the PDF/Langtag for accessibility. - Dynamic page numbers:
{{pageNumber}}and{{totalPages}}in any text element.
Register TrueType fonts globally or per-document:
import { Font, Document, Text } from '@formepdf/react';
import { renderDocument } from '@formepdf/core';
// Global registration (works like react-pdf)
Font.register({
family: 'Inter',
src: './fonts/Inter-Regular.ttf',
});
Font.register({
family: 'Inter',
src: './fonts/Inter-Bold.ttf',
fontWeight: 'bold',
});
const pdf = await renderDocument(
<Document>
<Text style={{ fontFamily: 'Inter', fontSize: 16 }}>
Regular text
</Text>
<Text style={{ fontFamily: 'Inter', fontSize: 16, fontWeight: 'bold' }}>
Bold text
</Text>
</Document>
);Or pass fonts directly on the Document:
<Document fonts={[
{ family: 'Roboto', src: './fonts/Roboto-Regular.ttf' },
{ family: 'Roboto', src: './fonts/Roboto-Italic.ttf', fontStyle: 'italic' },
]}>Font sources can be file paths, data URIs, or Uint8Array. Fonts are automatically subsetted — only glyphs used in the document are embedded.
| Component | Description |
|---|---|
<Document> |
Root element. title, author, lang, fonts. |
<Page> |
A page. size, margin (number, string, array, or edges). |
<View> |
Container. Flexbox layout. href, bookmark. |
<Text> |
Text content. Fonts, sizes, colors. href, bookmark. |
<Image> |
JPEG or PNG. href, alt. Aspect ratio preserved. |
<Table> |
Table with column definitions. |
<Row> |
Table row. header for repeating on page breaks. |
<Cell> |
Table cell. colSpan, rowSpan. |
<Svg> |
Inline SVG graphics. href, alt. |
<Fixed> |
Repeating header or footer. |
<PageBreak> |
Force a page break. |
| Forme | react-pdf | Puppeteer | |
|---|---|---|---|
| Page breaks | Page-native (widow/orphan aware) | Broken for 7 years | CSS page-break (fragile) |
| Table header repetition | Automatic on every page | Not built in | Inconsistent <thead> |
| Live preview | Built-in dev server | Render to file | Run script, open file |
| Click-to-inspect | VS Code, Cursor, WebStorm | No | No |
| Render speed | ~28ms (4-page report) | ~100-500ms | ~1-5s (Chrome boot) |
| Memory per render | No browser process (WASM) | ~50-100MB | ~50-200MB |
| SVG | Basic shapes and paths | Yes | Full browser SVG |
| Links | href prop on Text/View/Image/Svg |
<Link> component |
HTML <a> tags |
| Bookmarks | bookmark prop on any element |
Yes | No |
| Inline text styling | Nested <Text> elements |
Nested <Text> elements |
HTML/CSS |
| Absolute positioning | Yes | Yes | Full CSS |
| Custom fonts | TTF with subsetting | Yes | Yes |
| Dependencies | None (WASM) | yoga-layout | Chrome/Chromium |
| Runs in-process | Yes | Yes | No (subprocess) |
See the templates/ directory for production-ready examples:
- Invoice
- Product Catalog
- Receipt
- Report
- Shipping Label
Full docs at docs.formepdf.com
Issues and PRs welcome.
MIT
