ScreenshotNeo

BlogHTML to image & PDF

Library to Convert HTML to PDF

Choose a browser PDF API for familiar web rendering, or a paged-media converter for publishing controls. Compare Puppeteer, Playwright, and Prince, then build and validate a reliable PDF workflow.

By the ScreenshotNeo team29 September 202610 min read

Library to Convert HTML to PDF

For HTML and CSS that should render like a browser page, start with Puppeteer or Playwright: both expose a page PDF API that uses print CSS by default. Choose between them based on the browser automation stack already used by your project and the PDF controls you need. For a publishing workflow built around paged media, evaluate Prince. There is no universal winner: test representative documents in your own environment before choosing.

Browser-generated PDFs use print styles unless you explicitly emulate screen media. This affects colors, visibility, layout, and page breaks. Decide whether the document is a browser page printed to paper or a publication with precise paged-media requirements; that distinction narrows the library choice quickly. See the official Puppeteer PDF API, Playwright Page API, and Prince User Guide.

1. Compare the main approaches

Approach Best fit Documented behavior to account for
Puppeteer Your application already uses Puppeteer or a Chromium page render fits the job. Page.pdf() uses print CSS. The API includes paper format and header/footer templates; its guide says PDF generation waits for fonts by default.
Playwright Your application already uses Playwright or you want its documented PDF controls. Page.pdf() returns a PDF buffer and uses print CSS. Options include format, dimensions, margins, backgrounds, page ranges, headers/footers, CSS page-size preference and tagged-PDF configuration.
Prince The document is a publishing workflow where paged-media features deserve focused evaluation. Its guide describes converting HTML/Markdown or XML styled with CSS into PDFs, and documents generated page content, graphics, scripting and server integration.
Other converters Your project has a stack-specific constraint that the above options do not meet. Verify current official docs, version, capabilities, license and release information. The research available for this guide does not support a detailed feature ranking for WeasyPrint or wkhtmltopdf.

These are capabilities documented by the projects, not comparative benchmark results. A feature list cannot tell you whether your invoice, report or catalog will paginate correctly. Rendering fidelity depends on the actual HTML, styles, assets, fonts, runtime and configuration you deploy.

2. Generate a PDF with Puppeteer

Install Puppeteer in a Node.js project using the current instructions in its official installation guide. The following example navigates to a page, writes a PDF, and closes the browser even if navigation or generation fails.

Browser PDF APIs apply print styles, so the print layout is part of the implementation.
Browser PDF APIs apply print styles, so the print layout is part of the implementation.
import puppeteer from 'puppeteer';

const url = process.argv[2] ?? 'https://example.com';
const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto(url, { waitUntil: 'networkidle0', timeout: 60_000 });
  await page.pdf({
    path: 'output.pdf',
    format: 'A4',
    printBackground: true,
    preferCSSPageSize: true,
    margin: { top: '18mm', right: '15mm', bottom: '18mm', left: '15mm' }
  });
} finally {
  await browser.close();
}

Run it with node render.mjs https://example.com. Save the example as render.mjs in a project configured for ES modules with Puppeteer installed. Adjust the wait condition to suit the page: networkidle0 can be unsuitable for pages with long-lived connections or continuously requested resources. If the page signals readiness explicitly, wait for that selector or application state instead of assuming network activity defines completion.

Use print styles to control what belongs on paper:

@page {
  size: A4;
  margin: 18mm 15mm;
}

@media print {
  .screen-only, nav, .no-print { display: none !important; }
  .page-break { break-before: page; }
  a { color: inherit; text-decoration: none; }
}

preferCSSPageSize lets CSS page-size rules take precedence. printBackground opts into printing background graphics; inspect the resulting colors because print output can differ from screen output. Puppeteer documents that colors are modified for printing by default. If the intended PDF should reflect screen media, emulate it before calling pdf(), as shown in the official API documentation. Check the current versioned API for the exact option names and defaults used by your installed release.

Header and footer templates are useful for repeating metadata such as a page number or report title. They have their own template markup and styling constraints, so validate them in a real multi-page output. Avoid relying on a browser screenshot of the page as a substitute for the PDF pipeline: the page PDF method applies print media by default.

3. Generate a PDF with Playwright

Playwright is also a browser-driven route. Install the package and browser binaries using the official getting-started instructions. This example saves the returned PDF buffer to a file:

import { chromium } from 'playwright';
import { writeFile } from 'node:fs/promises';

const url = process.argv[2] ?? 'https://example.com';
const browser = await chromium.launch();
try {
  const page = await browser.newPage();
  await page.goto(url, { waitUntil: 'networkidle', timeout: 60_000 });
  const pdf = await page.pdf({
    format: 'A4',
    printBackground: true,
    preferCSSPageSize: true,
    margin: { top: '18mm', right: '15mm', bottom: '18mm', left: '15mm' },
    displayHeaderFooter: true,
    headerTemplate: '<span></span>',
    footerTemplate: '<span style="font-size:8px"><span class="pageNumber"></span> / <span class="totalPages"></span></span>'
  });
  await writeFile('output.pdf', pdf);
} finally {
  await browser.close();
}

Run with node render.mjs https://example.com after saving as an ES module. Playwright exposes controls for paper format or explicit dimensions, margins, header/footer templates, printed backgrounds, page ranges, CSS page-size preference and tagged PDF output. Use only the controls your output needs, and consult the current Page API for option types and constraints.

When a document needs screen styling rather than print styling, explicitly emulate screen media before generating the PDF:

await page.emulateMedia({ media: 'screen' });
const pdf = await page.pdf({ path: 'screen-styled.pdf' });

PDF generation still needs a page layout suitable for paper. A screen-optimized design may overflow or scale unexpectedly when put onto fixed-size pages. For a print document, print CSS is generally the intentional design surface.

4. Build HTML-to-PDF for documents you own

For invoices, statements and reports, generating a document from application data gives you control over markup and avoids depending on an arbitrary third-party site. Keep the template deterministic: use explicit page size and margins, supply fonts and images from stable locations, and define how long tables and sections break across pages. Escape user-provided values before inserting them into HTML; do not concatenate untrusted input into markup or scripts.

  1. Define the output contract. Specify page dimensions, margins, background behavior, page numbering, selectable text expectations, and whether page ranges or a tagged PDF are needed.
  2. Create a print stylesheet. Use @page, print-specific visibility rules, and break properties. Avoid assuming a screen layout will naturally paginate well.
  3. Wait for actual readiness. Navigation completion does not guarantee that application data, images or web fonts have finished loading. Wait for a meaningful selector or app-ready signal where possible. Puppeteer documents font waiting during PDF generation, but your own dynamic content still needs a readiness strategy.
  4. Generate and inspect. Save the PDF, validate that it opens, and review page count, clipping, blank pages and text/image output.
  5. Repeat in the production runtime. Use the same browser version, fonts, assets, permissions and container configuration that will generate user-facing files.

5. Choose Prince for a paged-media workflow

Prince is a dedicated converter to evaluate when page composition is central to the product rather than an incidental print action. Its guide describes HTML/Markdown or XML styled with CSS and features such as generated page content, graphics, scripting and server-side integration. Those are vendor-documented capabilities; they do not establish that Prince is best for every workload or that a particular document will render as desired.

Choose a browser page renderer or a publishing converter according to the document’s layout needs.
Choose a browser page renderer or a publishing converter according to the document’s layout needs.

Make a small representative document pack before committing: one short page, one long document, a long table, a section with images, and any repeating headers or footnotes you need. Confirm the behavior against the current Prince documentation and your license and deployment requirements. The same validation discipline applies to any converter: compare the actual output you need, not marketing descriptions.

6. Validation checklist before shipping

  • Test short and long documents, including a document that ends just before and just after a page boundary.
  • Test long tables, wide content, unbreakable blocks and explicit page breaks.
  • Check fonts: missing glyphs, fallback fonts, font weights and whether the deployed environment can access the required font files.
  • Check local and remote images, SVGs, image aspect ratios and content that loads after navigation.
  • Inspect print colors and backgrounds, links, headers, footers, page numbers and margin collisions.
  • Verify the first page and last page, not only a middle page; look for accidental blank pages and clipped footers.
  • Confirm page ranges and paper sizes with the same options used in production.
  • For sensitive documents, review what external resources the renderer can request and restrict network access to only the assets it needs.
  • Review whether accessibility-related PDF output is required and whether the selected library and configuration provide it for your use case.
  • Repeat after changing browser or converter versions, CSS, fonts or deployment images.

7. Reliability, performance and cost decisions

Rendering a page requires a browser or converter runtime, document assets and enough time for content to become ready. The research cited here provides no comparable benchmark, so do not choose based on unsourced claims about which library is fastest or lightest. Measure using your actual templates, document sizes, concurrency and deployment environment.

For reliability, set navigation and job timeouts, close browser instances in a finally path, capture actionable errors, and distinguish navigation failures from PDF-generation failures. Avoid sharing mutable page state across unrelated jobs. If processing a queue, cap concurrency to fit the memory and CPU available to the renderer, then watch queue wait time, timeouts and failed output. These are operational recommendations, not claims about a particular tool’s measured behavior.

For cost, account for implementation and maintenance, runtime hosting, capacity, any commercial license, and the cost of correcting layout failures. Verify current license terms and deployment support directly with each project before adoption. This research did not establish prices or total-cost comparisons for Puppeteer, Playwright or Prince. Prefer a small proof of concept that uses representative pages and runs in the intended environment.

8. Troubleshooting common failures

Symptom Likely cause What to check
PDF has screen layout differences PDF generation uses print media by default. Add or correct @media print rules; use screen media emulation only if screen output is the intended result.
Background colors or images are missing Background printing is disabled or print CSS suppresses them. Enable the API’s background printing option and inspect print styles and the PDF itself.
Content is cut off at page edges Margins, fixed widths, or print layout do not fit the paper dimensions. Set page size and margins explicitly; test wide content and print-specific widths.
Late data or images are absent The page was printed before asynchronous rendering completed. Wait for a page-specific ready selector or application signal; check image and font loading.
Header or footer is absent or overlaps Template markup, margins, or reserved header/footer space is insufficient. Review the relevant API’s template requirements and increase page margins; validate with multiple pages.
Timeout during navigation Long-running requests keep the selected load condition from completing, or the page is slow/unavailable. Use an appropriate wait condition, wait for a specific element, inspect failed requests, and set a bounded timeout.
PDF generation fails in deployment Runtime or browser dependencies differ from local development. Follow the tool’s installation guidance for the target environment and test there with the same fonts and permissions.
Unexpected blank pages or splits Print break rules or element sizing interact poorly at page boundaries. Inspect break-before, break-inside and oversized blocks in a representative PDF.

9. ScreenshotNeo for the visual capture step

If the deliverable is a PDF of a website as it appears visually, or a screenshot is the actual need, [ScreenshotNeo](https://screenshotneo.com) is a hosted screenshot API and MCP server. It is not an HTML-to-PDF library: use Puppeteer, Playwright or a converter when you need selectable document text, flowing pages, print CSS or document layout control. ScreenshotNeo’s PDF endpoint is useful when you want a capture service to render a URL as a PDF without setting up a browser yourself. See the ScreenshotNeo API documentation.

Or skip the browser setup

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

These examples use the default image response. ScreenshotNeo can return PNG, JPEG, WebP or PDF; configure the requested output using the API docs. Cookie banners, newsletter popups and chat widgets are removed before the shot, and each cleanup step can be disabled. Bot checks, blank pages, timeouts and failed loads are not billed; cache hits are also free, and response headers report the page verdict and billing status. An MCP server provides take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients. The free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. Every feature is on every plan. If a hosted visual capture fits your task, sign up free for 1,000 screenshots a month with no card.

10. FAQ

Does HTML-to-PDF preserve selectable text?

Browser and converter output is a PDF document, but verify text selection and extraction in the actual output and workflow you plan to ship. The documentation reviewed here does not establish a universal guarantee for every page or configuration.

Can I use CSS to choose the paper size?

Both Puppeteer and Playwright document CSS page-size preference controls. Define the intended page size in print CSS and confirm how the installed API version resolves it against explicit PDF options.

Should I switch to Prince for every multipage document?

No. Evaluate it when the document needs a publishing-oriented paged-media workflow, then compare representative output and verify current licensing and deployment requirements.

Which library is fastest?

The available evidence does not establish a winner. Measure end-to-end generation for your pages, runtime, concurrency and readiness conditions.

Primary references