pdfjs-dist vs jspdf vs pdf-lib vs react-pdf vs pdfkit vs pdfmake vs @pdfme/common
Client-Side PDF Generation and Rendering in Web Applications
pdfjs-distjspdfpdf-libreact-pdfpdfkitpdfmake@pdfme/commonSimilar Packages:
Client-Side PDF Generation and Rendering in Web Applications

@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.

Npm Package Weekly Downloads Trend
3 Years
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
pdfjs-dist8,721,68152,78237.6 MB4864 days agoApache-2.0
jspdf8,693,06631,09530.1 MB1123 days agoMIT
pdf-lib3,103,3208,270-3124 years agoMIT
react-pdf2,838,82310,882303 kB25a month agoMIT
pdfkit1,767,13010,5796.09 MB3975 months agoMIT
pdfmake1,282,88612,22514 MB25118 days agoMIT
@pdfme/common65,7124,1031.63 MB1122 months agoMIT

Client-Side PDF Handling: Generation vs. Modification vs. Rendering

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.

📄 Core Purpose: What Each Library Actually Does

jspdf: Generate New PDFs Imperatively

jspdf 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 PDFs

pdf-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 PDFs

pdfjs-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 Control

pdfkit 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 Layout

pdfmake 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 PDFs

react-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 Ecosystem

This 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

🔧 Modifying Existing PDFs: Only One Real Choice

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));

🖼️ Rendering PDFs in the Browser: Two Paths

For displaying existing PDFs, you have two practical choices:

  1. pdfjs-dist: Full control over rendering logic. Use when you need custom UI (e.g., thumbnail navigation, text layer tweaks).
  2. 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>

📝 Generating New Documents: Three Approaches

Imperative Drawing (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;
});

Declarative Layout (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])
    }
  }]
};

Low-Level Control (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();

⚠️ Critical Limitations to Know

  • 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.

🧩 When to Combine Libraries

Real-world apps often need both generation and viewing:

  • Use pdf-lib to fill a template PDF, then react-pdf to preview the result.
  • Generate a report with 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>

✅ Summary: Match the Tool to Your Task

TaskBest Library
Generate simple PDFsjspdf
Generate complex reportspdfmake
Low-level PDF creationpdfkit
Modify existing PDFspdf-lib
View PDFs in vanilla JSpdfjs-dist
View PDFs in Reactreact-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).

How to Choose: pdfjs-dist vs jspdf vs pdf-lib vs react-pdf vs pdfkit vs pdfmake vs @pdfme/common
  • pdfjs-dist:

    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.

  • jspdf:

    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.

  • pdf-lib:

    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.

  • react-pdf:

    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.

  • pdfkit:

    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.

  • pdfmake:

    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.

  • @pdfme/common:

    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.

README for pdfjs-dist

PDF.js

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.