Check the current contract and validator results before you build on this.
Choose @react-pdf/renderer when React components are the layout model your team wants. Choose pdfmake when a declarative document definition fits the document. Choose jsPDF when direct drawing commands and browser delivery are useful. Choose Runstamp when a Node.js service needs a typed PDF operation, explicit result evidence, and the same operation interface used for other document formats. The right answer depends on the required runtime, layout vocabulary, artifact checks, and who will operate the renderer.
Separate the similarly named React packages
react-pdf and @react-pdf/renderer solve different jobs. react-pdf displays an existing PDF in a React app. The @react-pdf/renderer docs describe a renderer that creates PDFs from React components on the web and on servers.
This comparison is about @react-pdf/renderer. If the job is only to display a file that already exists, use a viewer comparison instead of a generation comparison.
Comparison summary
| Option | Primary model | Document creation surface | Operating responsibility |
|---|---|---|---|
@react-pdf/renderer | React components and styles | Web and server APIs documented by the project | Your application owns components, fonts, rendering, and artifact tests |
| pdfmake | Declarative document definition | Browser and server setup documented by the project | Your application owns definitions, fonts, rendering, and artifact tests |
| jsPDF | Imperative PDF drawing API | Browser and Node.js builds documented by the project | Your application owns coordinates, pagination, plugins, and artifact tests |
| Runstamp | Typed document schema and named operation | Qualified Node.js package and declared operation surfaces | Your application owns the document model; the selected Runstamp surface defines execution and result evidence |
Do not choose from download counts, repository stars, or a generic feature checklist. Those numbers change, and a listed feature does not prove that the longest customer table, required font, or destination viewer will work. Build one representative fixture and evaluate the output in the environment you will ship.
@react-pdf/renderer: React components
@react-pdf/renderer uses components such as Document, Page, View, and Text. Its styling model makes sense to teams that already express layout through React composition.
import { Document, Page, StyleSheet, Text, View } from "@react-pdf/renderer";
const styles = StyleSheet.create({
page: { padding: 40 },
title: { fontSize: 22, marginBottom: 12 },
row: { flexDirection: "row", justifyContent: "space-between" },
});
export function Invoice({ invoice }) {
return (
<Document>
<Page size="A4" style={styles.page}>
<Text style={styles.title}>Invoice {invoice.number}</Text>
{invoice.items.map((item) => (
<View style={styles.row} key={item.id}>
<Text>{item.name}</Text>
<Text>{item.price}</Text>
</View>
))}
</Page>
</Document>
);
}
This is a good fit when React is an intentional document-authoring dependency, components will be reused across documents, and the project’s supported web or server API matches the deployment.
Before committing, test font loading, page wrapping, very long strings, repeated headers, images, and the exact server framework integration. A React-like API does not make browser CSS and PDF layout identical. Use the current project documentation for the installed version rather than assuming a framework workaround remains necessary.
pdfmake: declarative document definitions
pdfmake accepts a document definition containing blocks such as text, columns, lists, and tables. The project publishes separate browser and server setup guidance in its official documentation.
const definition = {
content: [
{ text: `Invoice ${invoice.number}`, fontSize: 22, marginBottom: 12 },
{
table: {
headerRows: 1,
body: [
["Item", "Price"],
...invoice.items.map((item) => [item.name, item.price]),
],
},
},
],
};
This model is useful when application data maps naturally to a JSON-like tree and the document needs flow layout rather than manual coordinates. It also gives teams a serializable boundary between business data and renderer input.
Treat complex tables as acceptance fixtures. pdfmake issue reports document table-width, row-span, and oversized-row cases. An issue report is a useful test input, but it is not a failure rate. Reproduce the specific layout on the version you intend to deploy. The Runstamp and pdfmake comparison records the current task-level decision boundary without assuming either tool wins every PDF job.
jsPDF: imperative drawing
The jsPDF project documents browser and Node.js builds and exposes commands for text, paths, images, pages, and related PDF objects. A small file can be direct:
import { jsPDF } from "jspdf";
const pdf = new jsPDF({ unit: "pt", format: "a4" });
pdf.setFontSize(22);
pdf.text(`Invoice ${invoice.number}`, 48, 64);
let y = 96;
for (const item of invoice.items) {
pdf.setFontSize(11);
pdf.text(item.name, 48, y);
pdf.text(item.price, 420, y, { align: "right" });
y += 18;
}
pdf.save("invoice.pdf");
Imperative coordinates are appropriate when the layout is small, precise, and controlled by the application. They become more expensive when content length is unpredictable or when the document needs automatic page breaking, repeated table structures, or several writing systems. Plugins can add higher-level behavior, so evaluate the exact plugin and version as part of the architecture rather than attributing every plugin capability to core jsPDF.
Runstamp: a typed Node.js document operation
Runstamp’s current PDF package requires Node.js 18.14.1 or later. The public document schema uses meta, page, children, and optional accessibility settings. It does not expose a PDF chart node. When a chart is needed, generate a reviewed image or SVG through a separate charting step and include it under the renderer’s supported asset policy.
import { runstamp } from "@runstamp/sdk";
import { writeFile } from "node:fs/promises";
const result = await runstamp.operations.run<{ bytes: Uint8Array }>("pdf.render", {
meta: { title: `Invoice ${invoice.number}` },
page: { size: "A4", margin: 48 },
children: [
{ type: "heading", level: 1, value: `Invoice ${invoice.number}` },
{ type: "paragraph", value: "Payment is due within 30 days." },
],
});
if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
await writeFile("invoice.pdf", result.value.bytes);
The pdf.render operation page defines the checked operation boundary, and the PDF package guide documents direct package use. The engine supports text, layout, graphics, images, tables, links, and bookmarks within its declared schema. Local execution is server-oriented; do not claim compatibility with browser, Edge, Deno, or Workers runtimes without a qualified binding and a deployment test for that exact environment.
Runstamp separates rendering from pdf.validate. This lets a product define structural checks after generation and retain operation diagnostics, losses, hashes, or other evidence available on the chosen execution surface. Structural validation still does not prove visual fidelity, accessibility, or business correctness.
Runstamp also publishes separate operations for PPTX, DOCX, and XLSX. That provides a common operation interface across formats, not one interchangeable document schema. Each format has its own capabilities and preservation limits.
Use an acceptance fixture before deciding
Create one input that represents the actual production risk:
- the longest customer name and address;
- a table that reaches a page boundary;
- a missing optional image;
- every required font and writing system;
- an untrusted string containing markup-like characters;
- the maximum expected item count;
- a destination-specific requirement such as print margins or a named viewer.
For each candidate, record the package version, runtime, font files, input fixture, generation command, output hash, page count, warnings, and manual-review result. Repeat the test after a renderer, font, or template change.
| If the primary requirement is… | Start by evaluating… |
|---|---|
| React component composition | @react-pdf/renderer |
| A declarative flow document with tables | pdfmake |
| Direct drawing in a browser or Node.js process | jsPDF |
| A typed Node.js operation with separate validation and evidence | Runstamp |
| Rendering an existing HTML/CSS page | A browser-based PDF renderer |
| One schema that must work in every runtime | Reframe the requirement; verify a specific renderer and runtime pair |
Production checklist
- Pin the renderer, plugins, fonts, and templates.
- Validate application input before rendering.
- Bound document size, images, remote assets, and execution time.
- Avoid logging sensitive source content.
- Test representative page breaks and destination viewers.
- Record the final artifact hash and validation result.
- Treat a regenerated file as a new approval candidate.
- Re-run the fixture set after any dependency or font change.
Use the PDF output guide to map the required artifact checks, and use the security boundary when choosing between a local process and a managed execution surface.
Keep the document workflow in your product.
Pick an operation, run it on a real file, then embed review and release where your customers work.