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.

Updated · 6 min read

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

ApproachRunsText in the PDFBest for
window.print()User’s browserReal textLetting users save a page themselves
html2pdf.js / jsPDFUser’s browserOften an imageSimple client-only exports
Puppeteer / PlaywrightYour serverReal textFull control at volume
HTML to PDF APIA hosted serviceReal textServer-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.