@pdfme/common, jspdf, pdf-lib, pdfjs-dist, pdfkit, pdfmake, and react-pdf are JavaScript libraries that address different aspects of PDF handling in web applications. jspdf, pdf-lib, pdfkit, and pdfmake focus on generating or modifying PDF documents programmatically in the browser or Node.js. pdfjs-dist (Mozilla’s PDF.js) specializes in rendering existing PDFs as images or HTML in the browser. react-pdf is a React-specific wrapper around PDF.js for declarative PDF viewing. @pdfme/common provides shared utilities for the PDFme ecosystem, primarily used alongside @pdfme/generator and @pdfme/ui for template-based PDF generation and form filling.
Working with PDFs in web apps falls into three distinct categories: generating new documents, modifying existing ones, and rendering/viewing PDF content. The libraries in this comparison each specialize in one or more of these areas — and choosing the wrong tool leads to frustration. Let’s break down how they differ in practice.
jspdf: Generate New PDFs Imperativelyjspdf creates PDFs from scratch using an imperative, canvas-like API. You call methods like .text() or .rect() to draw content at specific coordinates.
import jsPDF from 'jspdf';
const doc = new jsPDF();
doc.text('Hello world!', 10, 10);
doc.save('output.pdf');
pdf-lib: Modify Existing PDFspdf-lib reads, alters, and writes PDFs. It can fill form fields, add pages, or merge documents — but doesn’t render them visually.
import { PDFDocument } from 'pdf-lib';
const existingPdfBytes = await fetch('/form.pdf').then(res => res.arrayBuffer());
const pdfDoc = await PDFDocument.load(existingPdfBytes);
const form = pdfDoc.getForm();
form.getTextField('name').setText('John Doe');
const modifiedPdfBytes = await pdfDoc.save();
pdfjs-dist: Render Existing PDFspdfjs-dist converts PDF pages into canvases or SVGs for display. It doesn’t create or edit PDFs.
import * as pdfjsLib from 'pdfjs-dist';
pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdf.worker.js';
const loadingTask = pdfjsLib.getDocument('/document.pdf');
const pdf = await loadingTask.promise;
const page = await pdf.getPage(1);
const viewport = page.getViewport({ scale: 1.5 });
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
await page.render({ canvasContext: context, viewport }).promise;
document.body.appendChild(canvas);
pdfkit: Generate PDFs with Low-Level Controlpdfkit offers granular control over PDF generation, supporting paths, gradients, and custom fonts. It’s stream-friendly for server-side use.
import PDFDocument from 'pdfkit';
import blobStream from 'blob-stream'; // for browser output
const doc = new PDFDocument();
const stream = doc.pipe(blobStream());
doc.fontSize(25).text('Here is a PDF document.', 100, 100);
doc.end();
stream.on('finish', () => {
const url = stream.toBlobURL('application/pdf');
window.open(url);
});
pdfmake: Declarative Document Layoutpdfmake uses a JSON-like structure to define documents, handling pagination and styling automatically.
import pdfMake from 'pdfmake/build/pdfmake';
import pdfFonts from 'pdfmake/build/vfs_fonts';
pdfMake.vfs = pdfFonts.pdfMake.vfs;
const docDefinition = {
content: [
{ text: 'My Report', style: 'header' },
'This is a sample report.'
],
styles: { header: { fontSize: 18, bold: true } }
};
pdfMake.createPdf(docDefinition).download('report.pdf');
react-pdf: React Component for Viewing PDFsreact-pdf wraps PDF.js into React components, simplifying integration into React apps.
import { Document, Page } from 'react-pdf';
function PdfViewer() {
return (
<Document file="/sample.pdf">
<Page pageNumber={1} />
</Document>
);
}
@pdfme/common: Shared Utilities for PDFme EcosystemThis package exports types and helper functions used by @pdfme/generator (for template-based PDF creation) and @pdfme/ui (for interactive form filling). It has no standalone functionality.
// Typically used internally by other PDFme packages
import { Template } from '@pdfme/common';
// Not used directly for PDF generation or rendering
If you need to fill form fields, add watermarks, or merge documents, pdf-lib is the only viable option among these libraries. None of the others support reading and altering existing PDF structures.
jspdf, pdfkit, and pdfmake generate new PDFs only.pdfjs-dist and react-pdf are viewers.@pdfme/common isn’t a PDF processor.// pdf-lib: Merge two PDFs
const [pdf1, pdf2] = await Promise.all([
PDFDocument.load(bytes1),
PDFDocument.load(bytes2)
]);
const mergedPdf = await PDFDocument.create();
const copiedPages = await mergedPdf.copyPages(pdf2, pdf2.getPageIndices());
copiedPages.forEach(page => mergedPdf.addPage(page));
For displaying existing PDFs, you have two practical choices:
pdfjs-dist: Full control over rendering logic. Use when you need custom UI (e.g., thumbnail navigation, text layer tweaks).react-pdf: Simplified React integration. Use when you’re in a React app and want minimal setup.Both rely on the same underlying engine (PDF.js), so rendering quality is identical. react-pdf just hides the canvas/SVG plumbing.
// react-pdf handles loading, scaling, and error states
<Document file="/doc.pdf" onLoadSuccess={({ numPages }) => console.log(numPages)}>
{[...Array(numPages)].map((_, i) => <Page key={i + 1} pageNumber={i + 1} />)}
</Document>
jspdf)Best for simple layouts where you control every coordinate. Becomes unwieldy for multi-page documents with dynamic content.
// jspdf: Manual line breaks and page management
const doc = new jsPDF();
let y = 10;
['Line 1', 'Line 2', 'Line 3'].forEach(line => {
if (y > 280) { doc.addPage(); y = 10; }
doc.text(line, 10, y);
y += 10;
});
pdfmake)Handles pagination, tables, and styling automatically. Ideal for reports, invoices, or any document with repeating structures.
// pdfmake: Automatic page breaks in tables
const docDefinition = {
content: [{
table: {
body: data.map(row => [row.id, row.name])
}
}]
};
pdfkit)Use when you need vector graphics, custom color spaces, or streaming output (e.g., sending PDFs directly from a Node.js server without buffering).
// pdfkit: Draw a custom path
const doc = new PDFDocument();
doc.moveTo(100, 100).lineTo(200, 200).stroke();
jspdf doesn’t support embedding existing PDF pages or form filling.pdfmake cannot modify existing PDFs — only generate new ones.pdfjs-dist and react-pdf cannot create PDFs; they’re viewers only.pdfkit in the browser requires bundler configuration for font support.@pdfme/common is not a standalone tool — it’s a dependency for other PDFme packages.Real-world apps often need both generation and viewing:
pdf-lib to fill a template PDF, then react-pdf to preview the result.pdfmake, save it as a Blob, and display it using pdfjs-dist.Example: Fill form → Preview
// Step 1: Modify with pdf-lib
const filledPdfBytes = await fillForm(templateBytes, data);
const blob = new Blob([filledPdfBytes], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
// Step 2: Preview with react-pdf
<Document file={url}>
<Page pageNumber={1} />
</Document>
| Task | Best Library |
|---|---|
| Generate simple PDFs | jspdf |
| Generate complex reports | pdfmake |
| Low-level PDF creation | pdfkit |
| Modify existing PDFs | pdf-lib |
| View PDFs in vanilla JS | pdfjs-dist |
| View PDFs in React | react-pdf |
| Work with PDFme templates | @pdfme/common* |
* Only as part of the broader PDFme ecosystem.
Choose based on whether you’re creating, changing, or showing PDFs — and never try to force a viewer into a generator role (or vice versa).
Choose pdfjs-dist when your primary goal is to display existing PDF files in the browser with high fidelity, including support for text selection, zooming, and accessibility. It’s the engine behind Firefox’s PDF viewer and is best paired with custom UI layers. Do not use it for generating new PDFs from scratch.
Choose jspdf when you need a battle-tested, imperative API for generating simple to moderately complex PDFs directly in the browser with minimal setup. It’s well-suited for reports, invoices, or documents where precise layout control via code is acceptable. Avoid it for advanced typography, complex layouts, or when working with existing PDF templates.
Choose pdf-lib when your use case involves modifying existing PDFs — such as filling form fields, adding annotations, merging documents, or extracting pages — in both browser and Node.js environments. Its API is modern and promise-based, making it ideal for workflows that require reading and altering PDF content programmatically.
Choose react-pdf when building a React application that needs to render existing PDF documents with minimal boilerplate. It abstracts PDF.js into React components like <Document> and <Page>, handling loading states and rendering automatically. Do not use it for PDF creation or modification — it’s purely a viewer.
Choose pdfkit when you need fine-grained, low-level control over PDF generation with support for vector graphics, custom fonts, and streaming output — especially in Node.js. While it works in the browser via bundlers, its API is more verbose and requires deeper understanding of PDF internals. Avoid it for quick document generation or if you prefer high-level abstractions.
Choose pdfmake when you want to define document structure declaratively using JSON-like objects, supporting tables, lists, headers/footers, and page breaks out of the box. It’s excellent for data-heavy reports with consistent styling and works well in both browser and Node.js. Avoid it if you need pixel-perfect design control or must manipulate existing PDFs.
Choose @pdfme/common only if you’re already using the PDFme ecosystem (@pdfme/generator or @pdfme/ui) for template-driven PDF generation with form fields. It’s not a standalone PDF creation or rendering library but rather a set of shared types and utilities. Avoid it for general-purpose PDF tasks outside the PDFme workflow.
PDF.js is a Portable Document Format (PDF) library that is built with HTML5. Our goal is to create a general-purpose, web standards-based platform for parsing and rendering PDFs.
This is a pre-built version of the PDF.js source code. It is automatically generated by the build scripts.
For usage with older browsers/environments, without native support for the
latest JavaScript features, please see the legacy/ folder.
Please see this wiki page for information about supported browsers/environments.
See https://github.com/mozilla/pdf.js for learning and contributing.