These libraries solve different parts of the PDF workflow. @react-pdf/renderer and react-pdf-html focus on creating new PDFs using React components. pdf-lib handles low-level editing and merging of existing files. react-pdf and pdfjs-dist are designed for viewing PDFs in the browser. Choosing the right tool depends on whether you need to generate, edit, or display documents.
The JavaScript ecosystem offers several tools for working with PDFs, but they serve very different purposes. Some libraries create new documents from scratch, others modify existing files, and some are built strictly for viewing. Mixing them up can lead to major architectural issues. Let's break down how @react-pdf/renderer, pdf-lib, pdfjs-dist, react-pdf, and react-pdf-html handle common tasks.
The first decision is whether you need to make a PDF, show a PDF, or change a PDF. These packages do not overlap much here.
@react-pdf/renderer creates new PDFs using React components.
<View> and <Text>.// @react-pdf/renderer: Create a new document
import { Document, Page, Text } from '@react-pdf/renderer';
const MyDoc = () => (
<Document>
<Page>
<Text>Hello World</Text>
</Page>
</Document>
);
react-pdf-html also creates new PDFs but allows standard HTML tags.
<div> and <p> instead of custom components.// react-pdf-html: Create with HTML syntax
import { HTML } from 'react-pdf-html';
const MyDoc = () => (
<HTML>
<div>
<p>Hello World</p>
</div>
</HTML>
);
pdf-lib modifies or creates PDFs using a low-level API.
// pdf-lib: Modify or create low-level
import { PDFDocument } from 'pdf-lib';
const doc = await PDFDocument.create();
const page = doc.addPage();
page.drawText('Hello World');
react-pdf displays existing PDFs in the browser.
// react-pdf: Display an existing file
import { Document, Page } from 'react-pdf';
function Viewer() {
return <Document file="/invoice.pdf"><Page pageNumber={1} /></Document>;
}
pdfjs-dist provides the engine for displaying PDFs.
react-pdf.// pdfjs-dist: Low-level viewing
import * as pdfjsLib from 'pdfjs-dist';
const loadingTask = pdfjsLib.getDocument('/invoice.pdf');
const pdf = await loadingTask.promise;
const page = await pdf.getPage(1);
How you define content varies wildly between these tools. This affects developer speed and learning curve.
@react-pdf/renderer uses a subset of CSS flexbox.
// @react-pdf/renderer: Styling
const styles = StyleSheet.create({
page: { padding: 30 },
text: { fontSize: 12 }
});
<Text style={styles.text}>Content</Text>
react-pdf-html uses standard CSS classes.
// react-pdf-html: Standard CSS
<div className="container">
<p className="text">Content</p>
</div>
// CSS: .container { padding: 30px; }
pdf-lib uses imperative drawing commands.
// pdf-lib: Imperative drawing
page.drawText('Content', {
x: 50,
y: 500,
size: 12
});
react-pdf does not define content syntax.
// react-pdf: Viewer configuration
<Document file="/file.pdf">
<Page pageNumber={1} scale={1.5} />
</Document>
pdfjs-dist requires manual canvas rendering.
// pdfjs-dist: Canvas rendering
const viewport = page.getViewport({ scale: 1.5 });
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
await page.render({ canvasContext: ctx, viewport }).promise;
Sometimes you need to edit a file someone else made. This is where creation tools fail and editing tools shine.
@react-pdf/renderer cannot edit existing PDFs.
// @react-pdf/renderer: Not supported
// Cannot load an existing PDF to modify it
// Must recreate the document from scratch
react-pdf-html cannot edit existing PDFs.
// react-pdf-html: Not supported
// Cannot load an existing PDF to modify it
// Must recreate the document from scratch
pdf-lib excels at modifying existing files.
// pdf-lib: Load and modify
const existingPdfBytes = await fetch('/form.pdf').then(res => res.arrayBuffer());
const doc = await PDFDocument.load(existingPdfBytes);
const form = doc.getForm();
form.getTextField('name').setText('John Doe');
react-pdf loads existing files for viewing only.
// react-pdf: Load for viewing
<Document file="/existing-form.pdf">
<Page pageNumber={1} />
</Document>
pdfjs-dist loads existing files for viewing only.
// pdfjs-dist: Load for viewing
const loadingTask = pdfjsLib.getDocument('/existing-form.pdf');
const pdf = await loadingTask.promise;
// No save method available
Where your code runs matters. Some libraries work everywhere, while others are tied to the DOM.
@react-pdf/renderer works in Node.js and Browser.
// @react-pdf/renderer: Server or Client
// Works in Next.js API routes or browser components
const blob = await pdf(<MyDoc />).toBlob();
react-pdf-html works in Node.js and Browser.
// react-pdf-html: Server or Client
// Works similarly to renderer
const blob = await pdf(<MyDoc />).toBlob();
pdf-lib works in Node.js and Browser.
// pdf-lib: Universal
// Runs in Node.js scripts or browser bundles
const pdfBytes = await doc.save();
react-pdf is designed for the Browser.
// react-pdf: Browser focused
// Needs window/document for canvas rendering
// SSR requires disabling canvas rendering
pdfjs-dist works in Node.js and Browser.
// pdfjs-dist: Universal core
// Can parse PDFs in Node, render in Browser
pdfjsLib.GlobalWorkerOptions.workerSrc = '...';
| Package | Primary Use | Syntax Style | Edit Existing? | Environment |
|---|---|---|---|---|
@react-pdf/renderer | Create New | React Components | β | Node & Browser |
react-pdf-html | Create New | HTML/CSS | β | Node & Browser |
pdf-lib | Edit/Merge | Imperative API | β | Node & Browser |
react-pdf | View Only | React Components | β | Browser |
pdfjs-dist | View Only | Low-Level API | β | Node & Browser |
Pick the tool that matches your goal.
For Creating Documents: Use @react-pdf/renderer for most React projects. It is stable and well-supported. Use react-pdf-html only if you need standard HTML CSS support and accept the risk of community maintenance.
For Editing Documents: Use pdf-lib. It is the only choice here for merging, splitting, or filling forms programmatically.
For Viewing Documents: Use react-pdf. It saves you from setting up pdfjs-dist manually. Only drop down to pdfjs-dist if you need a highly custom viewer interface.
Final Thought: These libraries do not compete β they complement each other. A common pattern is using @react-pdf/renderer to generate a report, pdf-lib to stamp it with a signature, and react-pdf to let the user download or view it.
Choose @react-pdf/renderer if you need to generate dynamic PDFs like invoices or reports using React components. It offers a stable API with CSS-like styling and works in both Node.js and the browser. This is the standard choice for creation tasks in the React ecosystem.
Choose pdf-lib if you need to modify existing PDFs, such as filling forms, merging documents, or adding watermarks. It provides low-level control and works in Node.js and the browser. It is not designed for creating complex layouts from scratch using React components.
Choose pdfjs-dist if you need to build a custom PDF viewer from scratch. It provides low-level access to rendering PDF pages to canvas. This requires more setup than wrapper libraries but offers maximum control over the viewing experience.
Choose react-pdf if you want to display PDFs in a React app without building a viewer from scratch. It wraps pdfjs-dist with easy-to-use components like <Document> and <Page>. This is the fastest way to add PDF viewing to a React project.
Choose react-pdf-html if you want to generate PDFs using standard HTML and CSS syntax instead of custom components. Be aware that this relies on community maintenance and may lag behind @react-pdf/renderer updates. It is best for simple documents where HTML fidelity is key.
React renderer for creating PDF files on the browser and server
yarn add @react-pdf/renderer
import React from 'react';
import { Document, Page, Text, View, StyleSheet } from '@react-pdf/renderer';
// Create styles
const styles = StyleSheet.create({
page: {
flexDirection: 'row',
backgroundColor: '#E4E4E4',
},
section: {
margin: 10,
padding: 10,
flexGrow: 1,
},
});
// Create Document Component
const MyDocument = () => (
<Document>
<Page size="A4" style={styles.page}>
<View style={styles.section}>
<Text>Section #1</Text>
</View>
<View style={styles.section}>
<Text>Section #2</Text>
</View>
</Page>
</Document>
);
Web. Render in DOMimport React from 'react';
import ReactDOM from 'react-dom';
import { PDFViewer } from '@react-pdf/renderer';
const App = () => (
<PDFViewer>
<MyDocument />
</PDFViewer>
);
ReactDOM.render(<App />, document.getElementById('root'));
Node. Save in a fileimport React from 'react';
import ReactPDF from '@react-pdf/renderer';
ReactPDF.render(<MyDocument />, `${__dirname}/example.pdf`);
This project exists thanks to all the people who contribute. Looking to contribute? Please check our [contribute] document for more details about how to setup a development environment and submitting code.
Thank you to all our sponsors! [Become a sponsors]
Thank you to all our backers! [Become a backer]
MIT Β© Diego Muracciole