Generating a PDF from a React app comes down to three libraries: react-pdf for client-side rendering (good for invoices, simple reports), Puppeteer for server-side rendering (good for complex layouts, pixel-perfect fidelity), or jsPDF for simple programmatic PDFs (good for data exports with charts and tables).
Table of contents
- The three approaches
- The react-pdf approach
- The Puppeteer approach
- The jsPDF approach
- What usually breaks
- The right tool by use case
- The performance comparison
- FAQ
The three approaches
Each approach fits a different use case:
react-pdf(renders React components to PDF in the browser): Right for invoices, reports, simple documents. No server round-trip.- Puppeteer / Playwright (renders the React app to HTML, then prints to PDF): Right for complex layouts, full-page screenshots, pixel-perfect fidelity. Requires a Node.js server.
jsPDF(programmatic PDF generation in JavaScript): Right for data exports, simple charts, programmatic layouts. Lightweight, no React.
The react-pdf approach
The right tool for most client-side PDFs:
npm install @react-pdf/renderer
import { Document, Page, Text, View, PDFViewer } from '@react-pdf/renderer';
function Invoice({ items, total }) {
return (
<Document>
<Page size="A4">
<View>
<Text>Invoice</Text>
{items.map(item => <Text key={item.id}>{item.name}: ${item.price}</Text>)}
<Text>Total: ${total}</Text>
</View>
</Page>
</Document>
);
}
Use <PDFViewer> to preview in the browser, or <PDFDownloadLink> to trigger a download. The team that wants to generate the PDF server-side uses renderToStream from @react-pdf/renderer in a Node.js endpoint.
The Puppeteer approach
The right tool for complex layouts:
npm install puppeteer
const puppeteer = require('puppeteer');
async function generatePDF(html) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(html);
const pdf = await page.pdf({ format: 'A4' });
await browser.close();
return pdf;
}
The team that wants the React app to render exactly as it does in the browser uses this. The downside: Puppeteer is heavy (it ships a Chromium), so it doesn’t fit in a serverless function easily. The team that runs Puppeteer on RunxBuild or a dedicated Node.js host has the right architecture.
The jsPDF approach
The right tool for programmatic PDFs:
npm install jspdf
import jsPDF from 'jspdf';
const doc = new jsPDF();
doc.text('Invoice', 10, 10);
doc.text(`Total: $${total}`, 10, 20);
doc.save('invoice.pdf');
The team that wants to generate the PDF programmatically (without rendering React) uses this. Good for data exports, simple charts. Not good for complex layouts.
What usually breaks
The four pitfalls:
- Fonts don’t load. Custom fonts need to be embedded in the PDF, not loaded from a CDN. The team that uses Google Fonts in their React app and renders to PDF gets a PDF with the wrong fonts.
- Images don’t render. Cross-origin images need CORS or to be inlined as data URLs.
- CSS doesn’t match.
react-pdfhas its own CSS subset; not all CSS works. - Puppeteer in serverless. Puppeteer ships Chromium (~200MB); most serverless platforms can’t run it. The team that uses Puppeteer needs a dedicated host.
The right tool by use case
A clearer decision matrix:
| Use case | Tool |
|---|---|
| Invoice / receipt | react-pdf |
| Report with charts | jsPDF + a chart library |
| Full-page render | Puppeteer |
| Form filling | pdf-lib |
| Server-side programmatic | pdfkit (Node.js) or reportlab (Python) |
| Existing PDF editing | pdf-lib or qpdf |
The team that picks the right tool for the use case ships faster than the team that uses one tool for everything.
The performance comparison
Real-world benchmarks:
- react-pdf (client-side). ~500ms for a 5-page invoice on a modern laptop.
- Puppeteer (server-side). ~2-3 seconds for a 5-page render (includes Chromium startup). Steady-state ~500ms per page after warmup.
- jsPDF (programmatic). ~50ms for a 5-page document.
- pdfkit (Node.js). ~20ms for a 5-page document.
The team that needs low latency (under 100ms) uses programmatic tools. The team that needs visual fidelity uses Puppeteer. The team that needs the right balance uses react-pdf.
FAQ
What’s the best React PDF library?
Depends on the use case. react-pdf for client-side rendering of React components. Puppeteer for server-side rendering of HTML. jsPDF for programmatic PDFs.
Can I generate a PDF on the client?
Yes, with react-pdf or jsPDF. The team that wants to keep the PDF generation off the server uses these.
How do I generate a PDF that matches my React app’s design?
Use Puppeteer or Playwright. Render the React app in a headless browser, then print to PDF. The team that uses this gets pixel-perfect fidelity.
Can I run Puppeteer in a serverless function?
Not easily. Puppeteer ships Chromium (~200MB), and most serverless platforms have a 50MB deployment limit. The team that wants to run Puppeteer needs a dedicated Node.js host.
Can react-pdf render tables?
Yes, with <View> and styling. The team that needs complex tables uses the Table component from @react-pdf/renderer or builds it manually with <View> rows.
Does Puppeteer work in serverless?
Not easily. Puppeteer ships Chromium (~200MB); most serverless platforms have a 50MB deployment limit. The team that uses Puppeteer needs a dedicated Node.js host.
Can I generate a PDF in a browser extension?
Yes, with jsPDF or pdf-lib. Browser extensions have access to the same APIs as regular web pages.
If you are sizing the infrastructure for the kind of project this post covers, the RunxBuild hosting calculator is the right place to model the line items. The compute, the memory, the storage, the bandwidth, the database - each one is a separate number, and the team’s mental model for the platform is the sum of those numbers. The RunxBuild dashboard is where the team sees the actual usage in one place.
Useful related references: