| name | react-pdf |
| description | Generates PDF documents using the React-PDF library (@react-pdf/renderer) with TypeScript and JSX. Use when creating PDFs, generating reports, invoices, forms, resumes, or any document that needs flexbox layout, SVG graphics, custom fonts, or professional typesetting. Prefer over Python PDF libraries (ReportLab, fpdf2) when layout complexity matters. |
| allowed-tools | ["Bash","Read","Write","Grep","Glob"] |
Generating PDFs with React-PDF
When to Use
- Generating PDF documents (reports, invoices, resumes, forms, certificates)
- Creating PDFs that need complex layouts (multi-column, grids, cards)
- Building PDFs with inline SVG graphics, charts, or icons
- Producing documents with custom Google Fonts or emoji
- Any PDF task where flexbox layout is easier than absolute coordinate math
When NOT to Use
- Reading or extracting text from existing PDFs — use
pdfplumber or pypdf instead
- Filling existing PDF forms — use Python
pypdf or pdftk
- Converting HTML to PDF — use Playwright or WeasyPrint instead
- Simple one-off text PDFs with no layout needs — a Python script may be simpler
CRITICAL REQUIREMENTS
- Fonts MUST be local files - Remote font URLs (http/https) do NOT work. Always download fonts
to local files before using them.
- Wrap async code in IIFE - Top-level await causes errors. Always use
(async () => { ... })()
pattern.
- Disable hyphenation for custom fonts - Custom fonts lack hyphenation dictionaries and may
crash or break words incorrectly. Always call
Font.registerHyphenationCallback((word) => [word]); after registering custom fonts.
Files
{baseDir}/skills/react-pdf/references/google-fonts.txt - Metadata for ~65 popular Google Fonts with TrueType URLs. Each
line is a font variant in tab-separated format: font name, style, category, weight, url.
{baseDir}/skills/react-pdf/references/components.md - Full component API reference and supported CSS properties
{baseDir}/skills/react-pdf/assets/example-template.tsx - Minimal working example demonstrating fixed footers, page numbers,
and unbreakable content. Read this before starting to understand the basic patterns. Note: not all
APIs are shown here — always refer to the docs and {baseDir}/skills/react-pdf/references/components.md for the full API.
Prerequisites
npm install react @react-pdf/renderer
npm install -D tsx @types/react
tsx runs TypeScript + JSX files directly via Node with no config — no tsconfig.json needed. It
uses esbuild under the hood and handles JSX transformation automatically.
Core Components
- Document: Root component (metadata, settings)
- Page: Individual pages (A4, Letter, or custom dimensions)
- View: Container component (similar to div)
- Text: Text content, supports nesting for inline styling
- Image: Embed images (JPG, PNG, base64)
- Link: Clickable hyperlinks (external or internal)
- Note: Annotation notes
- Canvas: Freeform drawing with pdfkit methods
- Svg: Vector graphics (Circle, Rect, Path, Line, Polygon, etc.)
- StyleSheet: Create reusable styles
For full component props and CSS properties, see
references/components.md.
Basic Example
import React from "react";
import { Document, Page, Text, View, StyleSheet, renderToFile } from "@react-pdf/renderer";
const styles = StyleSheet.create({
page: { flexDirection: "column", backgroundColor: "#ffffff", padding: 40 },
title: { fontSize: 24, marginBottom: 20, fontWeight: "bold" },
text: { fontSize: 12, lineHeight: 1.5 },
});
const MyDocument = () => (
<Document>
<Page size="A4" style={styles.page}>
<View style={{ margin: 10, padding: 20 }}>
<Text style={styles.title}>Document Title</Text>
<Text style={styles.text}>Your content here</Text>
</View>
</Page>
</Document>
);
(async () => {
await renderToFile(<MyDocument />, "./output.pdf");
console.log("PDF saved!");
})();
Running Scripts
PDF generation scripts use JSX, which Node cannot run directly. Use tsx to execute them:
npx tsx my-document.tsx
npx tsx works without installing tsx globally — it downloads on demand. If tsx is installed as a
dev dependency (npm install -D tsx), it runs instantly without the npx download step.
Always wrap rendering in async IIFE:
(async () => {
await renderToFile(<MyDocument />, "./output.pdf");
})();
await renderToFile(<MyDocument />, "./output.pdf");
Previewing PDFs
To visually inspect generated PDFs, convert pages to images. Try pdftoppm first (often
pre-installed), fall back to Python's PyMuPDF if unavailable.
Option 1: pdftoppm (poppler-utils) — preferred, no install needed in many environments:
pdftoppm -png -r 200 document.pdf preview
Option 2: PyMuPDF (Python) — fallback if pdftoppm is not available:
pip install pymupdf
import fitz
doc = fitz.open("document.pdf")
for i, page in enumerate(doc):
pix = page.get_pixmap(dpi=200)
pix.save(f"page-{i+1}.png")
Rendering Methods
import { renderToFile, renderToBuffer } from "@react-pdf/renderer";
(async () => {
await renderToFile(<MyDocument />, "./document.pdf");
})();
(async () => {
const buffer = await renderToBuffer(<MyDocument />);
})();
Styling
Three methods: StyleSheet.create(), inline objects, or mixed arrays.
const styles = StyleSheet.create({ container: { padding: 20 } });
<View style={styles.container} />
<View style={{ padding: 20 }} />
<View style={[styles.container, { marginTop: 10 }]} />
Supported Units
pt (default, 72 DPI), in, mm, cm, %, vw, vh
Common Style Properties
{
flexDirection: "row", justifyContent: "space-between", alignItems: "center",
flexWrap: "wrap", gap: 10,
margin: 10, padding: 20, width: "100%", height: 200,
borderWidth: 1, borderColor: "#333", borderRadius: 5, borderStyle: "solid",
backgroundColor: "#f0f0f0", color: "#000", opacity: 0.8,
fontSize: 12, fontWeight: "bold", fontFamily: "Helvetica", fontStyle: "italic",
lineHeight: 1.5, textAlign: "center", textDecoration: "underline",
textTransform: "uppercase", letterSpacing: 1,
position: "absolute", top: 0, left: 0, right: 0, bottom: 0, zIndex: 10,
transform: "rotate(45deg)", transformOrigin: "center",
}
Images
Local files are most reliable. Remote URLs may fail due to network/CORS issues.
import { Image } from '@react-pdf/renderer';
<Image src="./images/photo.jpg" style={{ width: 200, height: 150 }} />
<Image src={{ data: buffer, format: 'png' }} />
SVG files cannot be used as Image sources. Read the SVG source and recreate using react-pdf Svg
components.
SVG Graphics
import { Svg, Circle, Rect, Path, Line, G, Defs, LinearGradient, Stop } from "@react-pdf/renderer";
<Svg width="200" height="200" viewBox="0 0 200 200">
<Defs>
<LinearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
<Stop offset="0%" stopColor="#3498db" />
<Stop offset="100%" stopColor="#9b59b6" />
</LinearGradient>
</Defs>
<Circle cx="100" cy="100" r="50" fill="url(#grad1)" />
<Rect x="10" y="10" width="50" height="50" fill="#e74c3c" />
<Path d="M10,50 Q50,10 90,50" stroke="#2ecc71" strokeWidth="2" fill="none" />
</Svg>;
Using Icons
Read SVG source from icon libraries and convert to react-pdf Svg components:
npm install lucide-static
import { Svg, Path, Rect } from "@react-pdf/renderer";
const MailIcon = ({ size = 12, color = "#888" }) => (
<Svg width={size} height={size} viewBox="0 0 24 24">
<Path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" stroke={color} strokeWidth={2} fill="none" />
<Rect x="2" y="4" width="20" height="16" rx="2" stroke={color} strokeWidth={2} fill="none" />
</Svg>
);
Links and Navigation
<Link src="https://example.com"><Text>Visit website</Text></Link>
<View id="section-1"><Text>Target</Text></View>
<Link src="#section-1"><Text>Jump to Section 1</Text></Link>
Dynamic Content and Page Numbers
<Text render={({ pageNumber, totalPages }) => `Page ${pageNumber} of ${totalPages}`} />
Fixed Headers/Footers
<Page size="A4">
<View fixed style={{ position: "absolute", top: 20, left: 30, right: 30 }}>
<Text>Header</Text>
</View>
<View style={{ marginTop: 60, marginBottom: 60 }}>
<Text>Content</Text>
</View>
<Text
fixed
style={{ position: "absolute", bottom: 20, left: 30, right: 30, textAlign: "center" }}
render={({ pageNumber, totalPages }) => `Page ${pageNumber} of ${totalPages}`}
/>
</Page>
Page Breaks and Wrapping
<View break />
<View wrap={false}><Text>Keep together</Text></View>
<Text orphans={2} widows={2}>Long text...</Text>
<View minPresenceAhead={100}><Text>Content</Text></View>
Custom Fonts
CRITICAL: All font sources MUST be local file paths. Remote URLs do not work.
import { Font } from "@react-pdf/renderer";
Font.register({
family: "Roboto",
fonts: [
{ src: "./fonts/Roboto-Regular.ttf", fontWeight: "normal" },
{ src: "./fonts/Roboto-Bold.ttf", fontWeight: "bold" },
{ src: "./fonts/Roboto-Italic.ttf", fontStyle: "italic" },
],
});
Font.registerHyphenationCallback((word) => [word]);
Built-in fonts: Courier, Helvetica, Times-Roman (each with Bold, Italic/Oblique variants)
Font weight values: thin (100), ultralight (200), light (300), normal (400), medium (500),
semibold (600), bold (700), ultrabold (800), heavy (900)
Google Fonts
Use {baseDir}/skills/react-pdf/references/google-fonts.txt to find font URLs, then download locally:
grep "^Roboto" {baseDir}/skills/react-pdf/references/google-fonts.txt | grep "700" | grep "normal"
mkdir -p fonts
curl -sL "<url-from-grep>" -o fonts/Roboto-Bold.ttf
file fonts/Roboto-Bold.ttf
If file shows "HTML document" or "ASCII text", the download failed. Try a different URL or search
GitHub for the font's official repo with TTF files.
Emoji
Emoji won't render in PDFs unless you register an emoji source. Install twemoji-emojis to get
local Twemoji PNG assets — no internet needed at render time.
npm install twemoji-emojis
import { Font } from "@react-pdf/renderer";
Font.registerEmojiSource({
format: "png",
url: "node_modules/twemoji-emojis/vendor/72x72/",
});
Then use emoji directly in Text: <Text>Hello</Text>
Other Features
<Canvas style={{ width: 200, height: 200 }}
paint={(painter, w, h) => { painter.circle(w/2, h/2, 50).fill("#3498db"); }} />
<Note style={{ color: "yellow" }}>Annotation text</Note>
Font.registerHyphenationCallback((word) => [word]);
<View debug><Text debug>Debug text</Text></View>
<Document title="My Doc" author="Author" subject="Report" language="en-US" pdfVersion="1.5" />
Best Practices
- Use
StyleSheet.create() — define styles once and reuse
- Compress images before embedding, use
cache={true} for remote images
- Test page breaks — content may flow differently than expected
- Prefer flexbox over absolute positioning
- Use
fixed prop for headers/footers on every page
- Use
debug={true} to visualize element boundaries
- Wrap rendering in try-catch blocks
Common Issues
Text overflow: <Text style={{ width: 200, maxLines: 3, textOverflow: "ellipsis" }}>...</Text>
Missing fonts: Download locally and register with local file paths. Remote URLs will NOT work.
Unexpected page breaks: Use wrap={false} to keep content together, or <View break /> to
force breaks.