A team ships a PDF export built on jsPDF's .html() method. It looks right. Then someone tries to select the invoice total to copy it, and nothing highlights, because the text is not text. It is a picture of text.
That is what .html() does. It hands your DOM to html2canvas, which rasterizes the page into an image and embeds that in the PDF. IronPDF for Node.js renders the same HTML into real, selectable text, which is the difference this piece is about.
Full disclosure. We build IronPDF for Node.js at Iron Software, and this read looks at where jsPDF's .html() screenshot path costs a team and where IronPDF renders the same HTML as real text.
The .html() path photographs your page
-
It is a screenshot, not a rendering: jsPDF's
.html()produces a screenshot rather than a rendering, since it hands your DOM tohtml2canvasto rasterize into a canvas image, so the text is not selectable, not searchable, and not readable by a screen reader, and the file is larger than a text-based PDF of the same content. -
CSS fidelity is partial:
html2canvasbuilds from computed DOM information rather than a real rendering pass, so it ignoresz-indexand draws in DOM order, and a layout that depends on layering can come out wrong, with SVG backgrounds and complex gradients handled poorly. - Long documents hit a canvas ceiling: jsPDF cannot exceed the browser canvas limit, commonly around 32,767 pixels per side, so a sufficiently long document produces blank pages rather than an error explaining why.
What jsPDF does well
The screenshot path is one side of jsPDF. Drawing a document directly is the other, where the API is direct and well documented.
import { jsPDF } from "jspdf";
const doc = new jsPDF();
doc.text("Invoice #1042", 20, 20);
doc.text("Total due: $250.00", 20, 30);
doc.save("invoice.pdf");
It runs entirely in the browser, so there is no server in the loop, no API round trip, and no server compute to scale as usage grows, which nothing server-side can match because the whole point is that no server is involved. It is MIT-licensed, it is the most widely adopted PDF library in JavaScript, and it carries years of plugins, examples, and answers a newer library has not accumulated. For programmatic drawing with text and addImage, the API is direct and well documented.
The security record cuts two ways
jsPDF's own code has a dense CVE history, fourteen advisories against the package, mostly resource-exhaustion and denial-of-service issues via user-controlled input to methods like addImage, addMetadata, createAnnotation, and the Acroform module. The precise version matters, though, because every one is fixed as of the current 4.2.1 release, and the cadence has been fast, with several patched inside the same version line they were reported against. That reads as a library getting real scrutiny and responding to it, a better signal than a library with zero reported CVEs and no way to tell whether anyone is looking.
The html2canvas dependency behind .html() is the quieter half. Its last npm release shipped in January 2022 and its last commit in July 2024, not archived but not moving. If your generation goes through .html(), that is the piece doing the work, and it is the less active half of what most people call jsPDF. A general search also turns up a CVE attached to the html2canvas name, but it belongs to an unrelated WordPress plugin that shares part of the name, not this library.
Real rendering, and text that stays text
IronPDF renders HTML through Chromium rather than photographing it, so the output is real PDF content, the text stays selectable and searchable, z-index works, and there is no canvas ceiling to run into.
import { PdfDocument } from "@ironsoftware/ironpdf";
const invoice = await PdfDocument.fromHtml(
"<h1>Invoice #1042</h1><p>Total due: $250.00</p>"
);
await invoice.saveAs("invoice.pdf");
That produces selectable text on a real rendering pass, not an embedded image of the page. The HTML-to-PDF guide walks the path end to end.
jsPDF and IronPDF for Node.js, side by side
| Capability | jsPDF | IronPDF for Node.js |
|---|---|---|
| HTML to PDF |
.html(), via html2canvas
|
PdfDocument.fromHtml, real rendering |
| Text after conversion | Not selectable | Selectable and searchable |
| CSS fidelity on that path | Partial, ignores z-index | Full, via Chromium |
| Long-document conversion | Canvas size ceiling | No such limit |
| Own-code CVE history | Fourteen | None found |
Table 1. jsPDF and IronPDF for Node.js on the HTML-conversion path, from jsPDF's own documentation and the html2canvas dependency it relies on.
Which path is your jsPDF on?
The one case where none of this applies is a document drawn programmatically with text and addImage, never converted from HTML. That is a solid, well-supported use of jsPDF, though few teams stay only on that path, and it holds right up until the requirement is to turn existing HTML into a PDF, since .html() answers that with a screenshot rather than a rendering. That is where a lot of teams discover the gap only after shipping, because nothing fails loudly, the PDF comes out as an image of the page instead.
If you are converting HTML and the output needs to behave like a real PDF rather than an embedded image, that is the gap IronPDF for Node.js closes.
Is your jsPDF usage drawing programmatically, or converting HTML through .html()? Tell us in the comments, we suspect more people are on the second path than realise what it does under the hood.
jsPDF is an open-source project distributed under the MIT licence, and is not affiliated with Iron Software. The details above are drawn from jsPDF's own documentation, the html2canvas dependency, and the GitHub Advisory Database. If something has changed since, correct us in the comments.
Top comments (0)