Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Use Playwright’s Chromium browser, load your HTML, wait for the assets your document needs, and call page.pdf(). The essential options are printBackground: true for backgrounds, format: 'A4' or 'Letter' for paper size, and preferCSSPageSize: true when your stylesheet defines @page. Playwright generates PDFs with print CSS media by default.
What you need
- Node.js and a project directory.
- The Playwright package and its Chromium browser binary.
- An HTML file or local HTTP endpoint that can be opened by Chromium.
Playwright documents PDF generation for Chromium. Install the package and browser binaries in your project:
npm init -y
npm install playwright
npx playwright install chromium
The browser download is separate from the npm package. In CI, install the same browser revision used by the project; Playwright also documents a Chromium headless-shell option for CI-oriented setups.
Basic HTML-to-PDF conversion
Create convert.js and point file:// at an absolute path. The script below writes output.pdf and keeps page backgrounds.
#1 Best Overall
const path = require('node:path');
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
try {
const page = await browser.newPage();
const htmlPath = path.resolve(__dirname, 'document.html');
await page.goto(`file://${htmlPath}`, { waitUntil: 'load' });
await page.pdf({
path: 'output.pdf',
format: 'A4',
printBackground: true,
preferCSSPageSize: true
});
} finally {
await browser.close();
}
})();
Run it with node convert.js. The path option saves the PDF; page.pdf() also returns the generated PDF buffer if you need to upload it or send it elsewhere.
Wait for the document before printing
waitUntil: 'load' only covers the page load event. It does not define that application state, web fonts, lazy images, or external resources are ready. Add waits that match your document.
Wait for a document marker
await page.goto(`file://${htmlPath}`, { waitUntil: 'load' });
await page.locator('#report-ready').waitFor();
await page.pdf({ path: 'output.pdf', format: 'A4', printBackground: true });
Wait for network activity to settle
await page.goto('http://127.0.0.1:3000/report', { waitUntil: 'networkidle' });
await page.pdf({ path: 'output.pdf', format: 'A4', printBackground: true });
Use an application-specific readiness signal where possible. A fixed timeout can be a fallback, but it is less reliable than waiting for the actual content:
await page.waitForTimeout(500);
Control print and screen styling
Playwright’s PDF API uses print CSS media by default. Therefore, rules inside @media print apply, while screen-only rules may not. If the PDF should look like the screen, emulate screen media before printing:
await page.emulateMedia({ media: 'screen' });
await page.pdf({ path: 'output.pdf', printBackground: true });
Background graphics are disabled by default, so set printBackground: true. Chromium can adjust printed colors; when exact colors matter, add -webkit-print-color-adjust: exact to the relevant CSS.
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
@media print {
.screen-only { display: none; }
}
.print-color {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
Paper size, margins, scale and page ranges
Choose a standard format or specify dimensions. If both are supplied, format takes priority over width and height.
| Goal | Option | Example |
|---|---|---|
| Standard paper | format |
format: 'A4' or format: 'Letter' |
| Custom paper | width, height |
width: '210mm', height: '297mm' |
| Reserved print space | margin |
margin: { top: '20mm', bottom: '18mm', left: '15mm', right: '15mm' } |
| Resize content | scale |
Values from 0.1 through 2; default 1 |
| Selected pages | pageRanges |
pageRanges: '1-3,5' |
| Honor CSS paper rules | preferCSSPageSize |
true |
Define a CSS page size when the document owns its print geometry:
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →@page {
size: A4;
margin: 18mm 15mm;
}
Set preferCSSPageSize: true to let this @page declaration take priority. Otherwise, use the PDF option’s format and margins as the source of truth.
Headers, footers and page numbers
Enable templates with displayHeaderFooter: true. Playwright injects documented classes for date, title, URL, page number and total pages. Template scripts are not evaluated, and the page’s styles are not visible inside the templates, so include inline styles.
await page.pdf({
path: 'output.pdf',
format: 'A4',
printBackground: true,
displayHeaderFooter: true,
headerTemplate: '<div style="font-size:9px;width:100%;text-align:center">Internal report</div>',
footerTemplate: '<div style="font-size:9px;width:100%;text-align:center">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>'
});
Reserve enough top and bottom margin for these templates; otherwise content can overlap them.
Rank #3
Local files versus a local HTTP server
Use a file:// URL for static documents
A file URL is convenient when HTML, CSS and assets are local and do not require server routes. Resolve the path to an absolute location and use forward-slash URL syntax as shown in the basic example.
Serve the document over HTTP when the app expects it
A local server is often easier when the page uses relative URLs, JavaScript modules, client-side routing or server-rendered routes. Start your application, navigate to http://127.0.0.1:PORT/path, and apply the same readiness waits before calling page.pdf(). This also avoids file-origin assumptions in code that was built for a web origin.
Reusable conversion function
This version returns a buffer, allowing a caller to store the PDF itself or pass it to another API.
const { chromium } = require('playwright');
async function htmlToPdf(url, options = {}) {
const browser = await chromium.launch();
try {
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'load' });
await page.waitForSelector('[data-pdf-ready]');
return await page.pdf({
format: 'A4',
printBackground: true,
preferCSSPageSize: true,
...options
});
} finally {
await browser.close();
}
}
(async () => {
const pdf = await htmlToPdf('http://127.0.0.1:3000/report');
require('node:fs').writeFileSync('report.pdf', pdf);
})();
Troubleshooting common failures
The PDF is blank or missing content
- Cause: printing started before client-side rendering finished. Fix: wait for a selector or application-ready flag, not only
load. - Cause: a route or asset works over HTTP but not from
file://. Fix: serve the project locally and navigate to its HTTP URL.
Colors or backgrounds disappeared
Set printBackground: true. If print CSS intentionally removes colors, inspect the @media print rules and use emulateMedia({ media: 'screen' }) when screen styling is the desired result.
The page size is wrong
Check whether format is overriding width and height. If CSS @page should win, set preferCSSPageSize: true. Also check units and margins.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
Header or footer text is absent
Set displayHeaderFooter: true, use the documented template classes, and put styles directly in the template. Template JavaScript will not run.
Chromium will not launch
Install the browser binary with npx playwright install chromium. Avoid pointing executablePath at an arbitrary browser unless you understand the compatibility risks; Playwright cautions that this option should be used with extreme care.
Fonts or external images are incomplete
Wait for the application’s font/image readiness signal. A PDF call has no universal guarantee that arbitrary asynchronous resources are complete. For remote assets, verify the URL is reachable from the machine running Chromium and that the page does not depend on an unavailable session.
Performance and reliability choices
- Reuse a browser process for batches of documents, while creating a fresh page or context for isolation.
- Prefer explicit readiness selectors over long fixed delays.
- Keep output options consistent so page geometry does not change between runs.
- Close pages and browsers in
finallyblocks so failures do not leak processes. - Run the same Chromium installation in development and CI to reduce rendering differences.
- Use
pageRangeswhen a workflow needs only selected pages, rather than generating and trimming a larger file afterward.
Or skip the browser setup
If you need a hosted capture instead of maintaining Chromium locally, ScreenshotNeo accepts one GET request and can return a PDF. Its cleanup step accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. It also provides an MCP server with take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients.
For API parameters and PDF options, see the ScreenshotNeo documentation. Example request:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The Free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Best Value
FAQ
Can Playwright convert an HTML string without creating a file?
Yes. Create a page, call page.setContent(html), wait for your readiness condition, then call page.pdf(). Use a local HTTP URL instead when the document relies on routes or a web origin.
Which browser does Playwright PDF generation use?
The documented PDF workflow is for Chromium. Install the matching Playwright Chromium binary rather than assuming an arbitrary system browser is equivalent.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Can I return the PDF from an API endpoint?
Yes. Omit the path option, use the returned buffer, set an application/pdf response content type, and send the buffer to the client.
Frequently Asked Questions
Does page.pdf() use screen CSS by default?
No. It uses print CSS media by default; call page.emulateMedia({ media: ‘screen’ }) when the PDF must follow screen styles.
Why does my CSS @page size not apply?
A format option takes priority over width and height. Set preferCSSPageSize: true when the CSS @page declaration should control paper size.
Are header and footer templates able to run JavaScript?
No. Template scripts are not evaluated, and the document’s styles are not available inside the templates.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesThe Bottom Line
For a local, repeatable conversion, install Playwright’s Chromium binary, wait for the page’s real readiness signal, then use page.pdf() with explicit media, background, paper-size and margin settings.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

