Library / SDK
parallax/jsPDF avatar
parallax/jsPDF

jsPDF: client-side PDF generation and the browser API surface it depends on

Client-side JavaScript PDF generation for everyone.

31,298 stars4,792 forksJavaScriptMIT

At a glance

What is it?
jsPDF builds PDF files in JavaScript, in the browser or under Node. The install is one npm line; the interesting decisions are about fonts, optional dependencies and file access.
Who is it for?
Adopt jsPDF when the document is generated in the browser or in a Node script and you control the layout through the drawing API. Do not adopt it as a general HTML-to-PDF converter: the html method pulls in html2canvas and, for string input, dompurify, and the README advises sanitizing user input before it reaches the library.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem jsPDF solves: producing a PDF without a server round trip

A PDF is a binary container with a cross-reference table, page objects, font resources and a content stream. Producing one usually means a server process, a headless browser or a native library. jsPDF builds that structure in JavaScript, so the document can be assembled where the data already is: in the page, in a worker, or in a Node script. The README states the goal in one line, calling it a library to generate PDFs in JavaScript. The default constructor is a4 paper, portrait, using millimeters for units, and the first example writes a string at coordinates 10, 10 before calling save. That coordinate model tells you what kind of tool this is. You place text and shapes yourself rather than handing over markup and receiving a laid-out page. The audience is therefore developers who know the layout they want: invoice generators with fixed columns, label printers, report exporters, and anyone who needs a file the user can download without a backend renderer in the path.

How the document is built: constructor options, coordinates and save

The API is a drawing surface with a page buffer behind it. The constructor takes an options object; the README shows orientation set to landscape, unit set to in, and format set to a two-element array, [4, 2], producing a two-by-four-inch page. Text placement is explicit: doc.text("Hello world!", 1, 1) in that example, with the coordinates interpreted in the unit you selected. Output happens through doc.save, which names the file. Under Node the same call writes to the current working directory instead of triggering a browser download, and the README notes that requiring jspdf automatically loads the node build. The distribution reflects that split. The dist folder ships jspdf.es.*.js for ES2015 modules, jspdf.node.*.js for Node, and jspdf.umd.*.js for AMD or script tags, plus polyfills.*.js for older browsers. The README says it is usually not necessary to specify the exact file, because build tools or Node resolve the right one from the bare "jspdf" specifier. That resolution step is worth knowing about: the file you get differs by environment, and the Node build uses file operations for loading and saving rather than browser APIs.

Getting it running: npm, the unpkg script tag and the Node entry point

Installation is the standard two-liner. The README recommends npm install jspdf --save, with yarn add jspdf as the alternative, and offers a script tag against unpkg for the UMD build. In a module project you import the named export: import { jsPDF } from "jspdf", then construct, draw and save. Under Node the README switches to CommonJS: const { jsPDF } = require("jspdf"), with a comment stating that this will automatically load the node version, and doc.save writes a4.pdf into the current working directory. AMD projects use require(["jspdf"], ({ jsPDF }) => ...). Script-tag users read window.jspdf and destructure jsPDF from it. TypeScript is supported through a typings file that ships with the package, and the README mentions a Meteor package, jspdf:core, added with meteor add jspdf:core. For older browsers, importing jspdf/dist/polyfills.es.js loads the required polyfills; the umd polyfills variant is described as self-contained, while the es variant imports them from core-js.

Optional dependencies: html2canvas, dompurify and the bundle you did not plan for

The html method is the part of jsPDF that does not fit the coordinate model, and it is also the part with the heaviest dependencies. According to the README, html depends on html2canvas and, when supplied with a string HTML document, on dompurify. jsPDF loads these dynamically using the respective module format, and the README states that build tools like Webpack will automatically create separate chunks for each optional dependency. That is the trade-off: if you never call html, you still get chunk generation unless you intervene. The documented intervention is to declare the unused ones as externals in webpack.config.js, under an externals object listing canvg, html2canvas and dompurify, with a comment warning to define only the dependencies you are not using. The README gives per-toolkit pointers for the same job: configureWebpack or chainWebpack in vue.config.js, custom webpack builders for Angular, and react-app-rewired or ejecting for create-react-app. Note that canvg appears in the externals example, so a canvas-related optional path exists beyond the two named in the html description, though the README does not spell out which API needs it.

The Node file-access restriction and why the README prefers runtime flags

Under Node, jsPDF restricts reading files from the local file system by default. The README's strongly recommended approach is to let the runtime enforce access, using Node's permission flags: node --permission --allow-fs-read=... ./scripts/generate.js. There is a catch stated plainly in the text: you need to include all imported JavaScript files, including all dependencies, in the --allow-fs-read flag. That makes the flag list a maintenance item rather than a one-time setup, because adding a dependency can mean editing the command. The fallback is a property on the document instance, doc.allowFsRead, set to an array of globs and paths such as "./fonts/*" and "./images/logo.png". The README labels this not recommended and warns that the Node flags are enforced by the runtime and offer stronger security. If your generator embeds local fonts or images, this is the configuration surface you have to get right, and the library's own documentation points away from its own mechanism.

Where jsPDF is the wrong tool

Two cases stand out from the README. The first is HTML fidelity. If your source of truth is a styled page and you want the PDF to look like it, jsPDF is a drawing library with an html method bolted on through html2canvas, which is a different rendering path with its own dependency weight. The second is untrusted input. The README opens its security section with an instruction rather than a feature: sanitize user input before passing it to jsPDF. The library does not promise to neutralize what you hand it, and the dompurify dependency exists only for string HTML passed to html. A third case is older browsers. jsPDF requires modern browser APIs, and the polyfills files exist precisely because those APIs are absent in environments like Internet Explorer. If you cannot ship the polyfills, or you cannot accept the bundle cost of core-js imports in the es variant, the library will not run there. None of these are defects in the PDF writer itself; they are boundaries of the design.

The alternative to weigh: server-side rendering from HTML

The natural comparison is a headless browser driven from the server, which renders your existing HTML and CSS and prints the result to PDF. The difference in approach is where layout is decided. With jsPDF, layout is decided by your JavaScript calls to text, and the page geometry comes from the constructor options such as orientation, unit and format. With a headless browser, layout is decided by the CSS engine, and your job shifts to keeping the print stylesheet correct. That buys fidelity for complex documents and costs you a browser process per render, plus the operational work of running it. jsPDF's advantage is the opposite: no server component, no browser process, and the file is produced in the same environment that holds the data. The optional-dependency story is the honest dividing line. If you find yourself reaching for html and therefore for html2canvas and dompurify, you are paying part of the headless-browser cost in client bundle size instead of server CPU, and that is the point at which the server-side renderer deserves a second look.

Maintenance, licence and what to verify before adopting

jsPDF is MIT licensed, which permits commercial use and modification; the repository carries a LICENSE file, and this is a description of the licence identifier rather than legal advice. The project is not archived, is co-maintained according to the README by yWorks, and the release history shows v4.2.1, v4.2.0 and v4.1.0 arriving in the first quarter of 2026, with the last push to the default branch dated 2026-09-10. That cadence matters if you depend on the optional-dependency behaviour, because the externals list in your webpack.config.js is coupled to which dependencies jsPDF loads dynamically. An upgrade that adds or renames one of those modules can leave you generating a chunk you thought you had removed. The upgrade cost is otherwise low: the import specifier stays "jspdf", and resolution to the es, node or umd file is handled by your toolchain. The things to verify first are concrete. Check your bundler's output for html2canvas, dompurify and canvg chunks if you never call html. Check whether your target browsers need jspdf/dist/polyfills.es.js or the self-contained umd variant. Under Node, check that your --allow-fs-read list covers every imported file, including dependencies, or decide deliberately to use doc.allowFsRead and accept the weaker enforcement the README warns about.

Editorial conclusion

Adopt jsPDF when the document is generated in the browser or in a Node script and you control the layout through the drawing API. Do not adopt it as a general HTML-to-PDF converter: the html method pulls in html2canvas and, for string input, dompurify, and the README advises sanitizing user input before it reaches the library. Before shipping, verify the three things the documentation does not settle for you: which optional dependencies your build actually bundles, whether your target browsers need the polyfills file, and, under Node, whether the runtime permission flags cover every file your script reads.

Official sources

  1. License: MIT
  2. parallax/jsPDF on GitHub
  3. Project website
  4. README
  5. Releases
Community notes

Community notes