Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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
Sale
HTML and CSS: Design and Build Websites
  • 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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
@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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • 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 finally blocks so failures do not leak processes.
  • Run the same Chromium installation in development and CI to reduce rendering differences.
  • Use pageRanges when a workflow needs only selected pages, rather than generating and trimming a larger file afterward.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The 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.

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.