关于 react-pdf
使用 React-PDF 生成 PDF 文档的技能,适用于报告、表单及采用 Flexbox、图形或自定义字体的版式,并提供渲染、预览和故障排查指导。
主要功能
- 01创建多页 PDF 文档
- 02使用 Flexbox 组件组合版式
- 03嵌入图像、链接和 SVG 图形
- 04注册本地字体和表情资源
- 05预览渲染后的 PDF 页面
适用场景
- 01生成报告、发票或简历
- 02制作含网格、卡片或分栏的 PDF
- 03为 PDF 添加图表、图标或自定义字体
- 04通过图像预览检查页面布局
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ trailofbits/ skills-curated --skill react-pdf安装后在 Claude Code 中输入/react-pdf确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/example-template.tsxreferences/components.mdgoogle-fonts.txt详细介绍
使用 React-PDF 生成 PDF
适用场景
- 生成 PDF 文档(报告、发票、简历、表单、证书)
- 创建需要复杂布局的 PDF(多栏、网格、卡片)
- 构建包含内嵌 SVG 图形、图表或图标的 PDF
- 生成使用自定义 Google Fonts 或表情符号的文档
- 任何使用 flexbox 布局比通过绝对坐标计算更简单的 PDF 任务
不适用场景
- 读取或提取现有 PDF 中的文本——请改用
pdfplumber或pypdf - 填写现有 PDF 表单——请使用 Python
pypdf或pdftk - 将 HTML 转换为 PDF——请改用 Playwright 或 WeasyPrint
- 不需要布局的简单一次性文本 PDF——使用 Python 脚本可能更简单
重要要求
- 字体必须是本地文件——远程字体 URL(http/https)无法使用。使用字体前,请务必先将其下载到本地文件。
- 将异步代码封装在 IIFE 中——顶层 await 会导致错误。请始终使用
(async () => { ... })()模式。 - 使用自定义字体时禁用断字——自定义字体缺少断字词典,可能导致程序崩溃或错误地拆分单词。注册自定义字体后,务必调用
Font.registerHyphenationCallback((word) => [word]);。
文件
{baseDir}/skills/react-pdf/references/google-fonts.txt——约 65 种常用 Google Fonts 的元数据及 TrueType URL。每行是一个以制表符分隔的字体变体,格式为:font name、style、category、weight、url。{baseDir}/skills/react-pdf/references/components.md——完整的组件 API 参考和受支持的 CSS 属性{baseDir}/skills/react-pdf/assets/example-template.tsx——最简可运行示例,演示固定页脚、页码和不可拆分的内容。开始前请先阅读,以了解基本模式。注意:此处未展示所有 API——请始终参阅文档和{baseDir}/skills/react-pdf/references/components.md,了解完整 API。
前提条件
npm install react @react-pdf/renderer
npm install -D tsx @types/react
tsx 可通过 Node 直接运行 TypeScript + JSX 文件,无需配置,也不需要 tsconfig.json。它底层使用 esbuild,并会自动处理 JSX 转换。
核心组件
- Document:根组件(元数据、设置)
- Page:单个页面(A4、Letter 或自定义尺寸)
- View:容器组件(类似 div)
- Text:文本内容,支持嵌套以设置行内样式
- Image:嵌入图像(JPG、PNG、base64)
- Link:可点击的超链接(外部或内部)
- Note:注释
- Canvas:使用 pdfkit 方法进行自由绘图
- Svg:矢量图形(Circle、Rect、Path、Line、Polygon 等)
- StyleSheet:创建可复用样式
完整的组件属性和 CSS 属性请参阅 references/components.md。
基本示例
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!");
})();
运行脚本
PDF 生成脚本使用 JSX,Node 无法直接运行。请使用 tsx 执行:
npx tsx my-document.tsx
无需全局安装 tsx,npx tsx 会按需下载。如果将 tsx 安装为开发依赖(npm install -D tsx),它会立即运行,无需通过 npx 下载。
始终将渲染操作封装在异步 IIFE 中:
// Good
(async () => {
await renderToFile(<MyDocument />, "./output.pdf");
})();
// Bad - top-level await may fail
await renderToFile(<MyDocument />, "./output.pdf");
预览 PDF
若要直观检查生成的 PDF,请将页面转换为图像。先尝试 pdftoppm(通常已预装);如果不可用,再使用 Python 的 PyMuPDF。
选项 1:pdftoppm(poppler-utils)——首选,许多环境中无需安装:
pdftoppm -png -r 200 document.pdf preview
# → preview-1.png, preview-2.png, ...
选项 2:PyMuPDF(Python)——如果 pdftoppm 不可用,则使用此备用方案:
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")
渲染方法
import { renderToFile, renderToBuffer } from "@react-pdf/renderer";
// To file
(async () => {
await renderToFile(<MyDocument />, "./document.pdf");
})();
// To buffer
(async () => {
const buffer = await renderToBuffer(<MyDocument />);
})();
样式
有三种方式:StyleSheet.create()、行内对象或混合数组。
const styles = StyleSheet.create({ container: { padding: 20 } });
<View style={styles.container} />
<View style={{ padding: 20 }} />
<View style={[styles.container, { marginTop: 10 }]} />
支持的单位
pt(默认值,72 DPI)、in、mm、cm、%、vw、vh
常见样式属性
{
// Flexbox
flexDirection: "row", justifyContent: "space-between", alignItems: "center",
flexWrap: "wrap", gap: 10,
// Box model
margin: 10, padding: 20, width: "100%", height: 200,
// Borders
borderWidth: 1, borderColor: "#333", borderRadius: 5, borderStyle: "solid",
// Colors
backgroundColor: "#f0f0f0", color: "#000", opacity: 0.8,
// Typography
fontSize: 12, fontWeight: "bold", fontFamily: "Helvetica", fontStyle: "italic",
lineHeight: 1.5, textAlign: "center", textDecoration: "underline",
textTransform: "uppercase", letterSpacing: 1,
// Position
position: "absolute", top: 0, left: 0, right: 0, bottom: 0, zIndex: 10,
// Transforms
transform: "rotate(45deg)", transformOrigin: "center",
}
图片
本地文件最可靠。远程 URL 可能因网络或 CORS 问题而无法使用。
import { Image } from '@react-pdf/renderer';
<Image src="./images/photo.jpg" style={{ width: 200, height: 150 }} />
<Image src={{ data: buffer, format: 'png' }} />
不能将 SVG 文件用作图片源。 请读取 SVG 源码,并使用 react-pdf Svg 组件重新创建。
SVG 图形
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>;
使用图标
读取图标库中的 SVG 源码,并将其转换为 react-pdf Svg 组件:
npm install lucide-static
import { Svg, Path, Rect } from "@react-pdf/renderer";
// Converted from lucide-static/icons/mail.svg
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>
);
链接和导航
<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>
动态内容和页码
<Text render={({ pageNumber, totalPages }) => `Page ${pageNumber} of ${totalPages}`} />
固定页眉/页脚
<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>
分页和换行
<View break /> // Force page break
<View wrap={false}><Text>Keep together</Text></View> // Prevent breaking inside
<Text orphans={2} widows={2}>Long text...</Text> // Orphan/widow control
<View minPresenceAhead={100}><Text>Content</Text></View> // Min space before break
自定义字体
重要:所有字体源都必须是本地文件路径。 远程 URL 无法使用。
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" },
],
});
// Always disable hyphenation when using custom fonts
Font.registerHyphenationCallback((word) => [word]);
内置字体:Courier、Helvetica、Times-Roman(每种都提供 Bold、Italic/Oblique 变体)
字体粗细值:thin(100)、ultralight(200)、light(300)、normal(400)、medium(500)、 semibold(600)、bold(700)、ultrabold(800)、heavy(900)
Google Fonts
使用 {baseDir}/skills/react-pdf/references/google-fonts.txt 查找字体 URL,然后下载到本地:
# Find the font URL
grep "^Roboto" {baseDir}/skills/react-pdf/references/google-fonts.txt | grep "700" | grep "normal"
# Download
mkdir -p fonts
curl -sL "<url-from-grep>" -o fonts/Roboto-Bold.ttf
# Verify - must show "TrueType Font data"
file fonts/Roboto-Bold.ttf
如果 file 显示“HTML document”或“ASCII text”,说明下载失败。请尝试其他 URL,或在
GitHub 上搜索该字体的官方仓库,查找 TTF 文件。
Emoji
除非注册了 Emoji 源,否则 PDF 中无法渲染 Emoji。安装 twemoji-emojis 即可获取本地 Twemoji PNG 资源,无需在渲染时连接互联网。
npm install twemoji-emojis
import { Font } from "@react-pdf/renderer";
Font.registerEmojiSource({
format: "png",
url: "node_modules/twemoji-emojis/vendor/72x72/",
});
然后可直接在 Text 中使用 Emoji:<Text>Hello</Text>
其他功能
// Canvas drawing
<Canvas style={{ width: 200, height: 200 }}
paint={(painter, w, h) => { painter.circle(w/2, h/2, 50).fill("#3498db"); }} />
// Annotation notes
<Note style={{ color: "yellow" }}>Annotation text</Note>
// Hyphenation
Font.registerHyphenationCallback((word) => [word]); // disable
// Debug mode - visualize boundaries
<View debug><Text debug>Debug text</Text></View>
// Document metadata
<Document title="My Doc" author="Author" subject="Report" language="en-US" pdfVersion="1.5" />
最佳实践
- 使用
StyleSheet.create()——只定义一次样式并重复使用 - 嵌入图片前先压缩;对于远程图片,使用
cache={true} - 测试分页效果——内容的流动方式可能与预期不同
- 优先使用 flexbox,而不是绝对定位
- 使用
fixed属性,让页眉/页脚显示在每一页 - 使用
debug={true}可视化元素边界 - 使用 try-catch 块包裹渲染过程
常见问题
文本溢出:<Text style={{ width: 200, maxLines: 3, textOverflow: "ellipsis" }}>...</Text>
缺少字体:下载字体到本地,并使用本地文件路径注册。远程 URL 无法使用。
分页异常:使用 wrap={false} 将内容保持在一起,或使用 <View break /> 强制分页。