HTML to PDF in JavaScript: browser print, html2pdf.js, Puppeteer or an API
The trade-offs between client-side libraries, headless Chrome and an API, with working code for each.
There are four common ways to turn HTML into a PDF from JavaScript. They differ in where the work happens, whether text stays selectable, and how much you have to run yourself. Here’s how to choose.
At a glance
| Approach | Runs | Text in the PDF | Best for |
|---|---|---|---|
window.print() | User’s browser | Real text | Letting users save a page themselves |
| html2pdf.js / jsPDF | User’s browser | Often an image | Simple client-only exports |
| Puppeteer / Playwright | Your server | Real text | Full control at volume |
| HTML to PDF API | A hosted service | Real text | Server-side PDFs without running Chrome |
1. window.print()
document.querySelector("#save-pdf").addEventListener("click", () => window.print());Opens the browser’s print dialog, where users can choose Save as PDF. Output quality is excellent and it costs nothing, but you can’t control the result automatically: users pick the paper size, margins and headers, and you never receive the file.
2. html2pdf.js and jsPDF
import html2pdf from "html2pdf.js";
html2pdf()
.set({ filename: "report.pdf", jsPDF: { format: "a4" } })
.from(document.querySelector("#report"))
.save();These run entirely in the browser by drawing the page with html2canvas and placing the result in a PDF. That makes them easy to add, but the PDF content is typically an image of the page: text may not be selectable or searchable, files can be large, and output can be blurry or split mid-line at page boundaries. Fine for quick exports; not ideal for documents people will read, search or archive.
3. Puppeteer or Playwright (headless Chrome)
import puppeteer from "puppeteer";
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(html, { waitUntil: "networkidle0" });
await page.pdf({
path: "report.pdf",
format: "A4",
printBackground: true,
margin: { top: "10mm", right: "10mm", bottom: "10mm", left: "10mm" },
});
await browser.close();This is the same kind of engine as our converter: Chromium’s print layout with real, selectable text. You get every option, including headers and footers. The cost is operational: Chromium is a large dependency, uses significant memory per page, needs fonts installed on the server, and must be isolated carefully if it renders untrusted HTML or URLs, because a browser that can reach your internal network is a security risk.
4. An HTML to PDF API
const response = await fetch("https://htmltopdf.online/api/convert/html", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ html, settings: { paper: "A4", marginMm: 10 } }),
});
if (!response.ok) throw new Error((await response.json()).error.message);
const pdf = await response.arrayBuffer();You send HTML and get a Chromium-rendered PDF back, without running a browser yourself. Our API is free and needs no key, but it’s rate limited (10 conversions per minute per client) and has no uptime guarantee, so it suits scripts, prototypes and low-volume tools. Call it from server-side code: requests from other websites’ browser code are refused. Full details are in the API guide.
How to choose
- Users saving what they see:
window.print()with a good print stylesheet. - Quick client-side export where image output is acceptable: html2pdf.js.
- Invoices, reports or contracts generated by your app at volume: Puppeteer or Playwright on your own infrastructure.
- Occasional server-side PDFs without maintaining Chrome: an API.
Whichever you choose, the same print CSS applies: see the print CSS checklist and page breaks guide.