Asking Playwright for a PDF looks like this, and the import gives it away.
import { chromium } from "playwright";
const browser = await chromium.launch();
const page = await browser.newPage();
await page.setContent("<h1>Invoice #1042</h1><p>Total due: $250.00</p>");
await page.pdf({ path: "invoice.pdf", format: "A4" });
await browser.close();
Note the chromium import. page.pdf() runs only in headless Chromium and throws on Firefox and WebKit, which do not implement PDF export at all. Playwright's whole pitch is one API across three engines, and for the PDF that pitch does not apply. IronPDF for Node.js installs one engine for the job, which is what this piece comes down to.
Full disclosure. We build IronPDF for Node.js at Iron Software, and this read looks at where Playwright's cross-browser design adds nothing to a PDF and where IronPDF covers it from one engine.
Why the extra engines do nothing for the PDF
Playwright installs three rendering engines, and PDF export uses one of them. On a Firefox or WebKit page the page.pdf() call throws, so the cross-browser reach is already irrelevant to the task, and the cost of that shape lands in three places.
-
PDF export is Chromium-only —
page.pdf()throws on Firefox and WebKit by Playwright's own documentation, which do not implement PDF export at all, so two of the three engines never render a page for the task and the cross-browser guarantee stops at the one thing you asked for. -
A default install pulls all three engines —
npx playwright installdownloads Chromium, Firefox, and WebKit, several times the footprint of Chromium alone, and it does not run on Alpine out of the box, so a container spends disk and CI time on two engines the PDF path never touches. - It stops at the render — like every browser-automation tool, Playwright produces a PDF but cannot merge two files, split pages out of one, encrypt a document, or apply a signature, so any workflow needing those pairs it with a second library and maintains two dependencies for one task.
A common pattern is a team reaching for Playwright because the name reads as the modern default, without the cross-browser part ever mattering for what they are building. If nothing in the project runs against Firefox or WebKit, the reason to prefer Playwright over a Chromium-only tool disappears, and what is left is a three-engine testing framework installed to make documents, carrying a browser-download step, a container footprint, and a per-test isolation model into a pipeline that needed a single render.
Playwright and IronPDF for Node.js, side by side
| Capability | Playwright | IronPDF for Node.js |
|---|---|---|
| PDF generation | Chromium only, via page.pdf()
|
Supported, PdfDocument.fromHtml
|
| Engines installed | Chromium, Firefox, WebKit | One PDF engine |
| Firefox or WebKit PDF | Throws an error | One engine, always renders |
| Merge, split, sign | Not supported | Supported |
| Primary design purpose | Cross-browser testing | PDF generation |
Table 1. Playwright and IronPDF for Node.js on the PDF questions, from Playwright's own documentation.
One engine, built for the PDF
IronPDF renders HTML through one Chromium engine built for PDFs, with no Firefox or WebKit to download and no test-automation machinery around it.
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 renders a document from a single engine install, with no three-engine footprint and no per-test context lifecycle to work around. And because it is a PDF library rather than a browser, merging, splitting, or signing the result lives in the same call chain rather than a second dependency. The HTML-to-PDF guide runs through the setup step by step.
What Playwright is actually for
As a test runner, Playwright does its job well. It auto-waits for elements, keeps one API across Chromium, Firefox, and WebKit, and ships a trace viewer that makes debugging test failures straightforward, which is why testing teams choose it over building their own automation layer. It is Apache-2.0 licensed, and its own repository has no CVEs on record across Microsoft's advisories, NVD, and Snyk. A CVE search returns hits, but every one traces to something other than the core package, a downstream tool that depends on Playwright or a separate Microsoft product that shares the name, Azure Playwright Testing and the Playwright MCP Server among them. Microsoft ships releases on a fast cadence to close to 95,000 GitHub stars.
That whole profile is about testing, though. PDF export is a single Chromium-only method off to one side of it, so nothing that makes Playwright a strong test runner, the cross-browser reach, the isolation model, the trace viewer, carries into the PDF at all.
When Playwright is already your test runner
The one case for Playwright on PDFs is a project already running it as a test suite, where a page.pdf() call is a convenient extra on a tool the team already maintains. That is rarely why Playwright lands in a PDF pipeline, though, since a project reaching for it purely to make documents installs three engines and a test-isolation model for a task that only ever touches one of them.
If PDF generation is the actual goal rather than a side effect of your test runner, IronPDF for Node.js covers it from one engine, with the merge and sign steps a real document pipeline needs in the same library.
Is Playwright already your test runner with PDF export riding along, or did it come into the project only to make documents? Tell us in the comments, we are curious how many started with the second.
Playwright is an open-source project maintained by Microsoft, distributed under Apache-2.0, and is not affiliated with Iron Software. The details above are drawn from the project's own documentation. If something has changed since, correct us in the comments.
Top comments (0)