ScreenshotNeo

BlogComparisons

wkhtmltopdf vs Playwright for Rendering Modern Websites

For JavaScript-heavy websites, Playwright is usually the better starting point. Compare rendering, PDF output, setup, security, and deployment before choosing.

By the ScreenshotNeo team4 October 20269 min read

For modern, JavaScript-heavy websites, Playwright is usually the better starting point. It automates current Chromium, Firefox, and WebKit builds, and its page API can produce both PDFs and screenshots. wkhtmltopdf is a simpler command-line choice for controlled or legacy documents whose output has already been validated in your deployment environment.

Neither tool guarantees identical output on every page. Rendering depends on the engine and version, fonts, CSS, page state, and when the capture begins. Compare both against representative pages in the exact environment where you intend to run them.

1. What is the difference?

Area wkhtmltopdf Playwright
What it is A command-line HTML-to-PDF and image renderer built on Qt WebKit. Browser automation software that can drive Chromium, Firefox, and WebKit.
Typical workflow Run a binary with a page URL or input document and output options. Launch a browser, open a page, wait for the desired state, then call a page API such as page.pdf().
JavaScript-heavy pages Has JavaScript controls, delays, scripts, and wait options, but those do not establish compatibility with current web applications. Runs browser engines used for contemporary sites; the application still needs to be ready before capture.
PDF and screenshots PDF is its main workflow and it can also render images. The Page API supports PDF generation and screenshots. PDF generation uses print CSS media.
Version upkeep The official downloads page identifies 0.12.6 as its stable series and dates it June 11, 2020. Verify the exact binary and build you use. Supported browser versions update with Playwright releases. Keep the package and its installed browser binaries aligned.
Untrusted input The project warns against using it with untrusted HTML and recommends sanitizing supplied HTML and JavaScript. This comparison does not establish a complete security model. Isolate browser workloads and review current official guidance for your threat model.

Sources: wkhtmltopdf project and status, Playwright browser versions, Playwright Page API, and wkhtmltopdf command-line options.

2. Which one should you choose?

Choose Playwright when

  • The page renders important content with contemporary JavaScript.
  • You need to automate browser interactions or wait for a particular UI state before output.
  • You want to exercise Chromium, Firefox, or WebKit, or need both PDF and screenshot operations in a browser automation workflow.

Keep or evaluate wkhtmltopdf when

  • Your input is a stable, controlled page or document template.
  • The existing output meets your layout and pagination needs.
  • A simple command-line conversion fits your deployment better than managing browser automation.

The age and history of wkhtmltopdf’s rendering engine are reasons to validate it carefully, not proof that every deployment or document will fail. Confirm the exact binary, Qt build, fonts, CSS behavior, JavaScript settings, and output using your templates. There is no sourced head-to-head speed or fidelity benchmark that establishes a universal winner.

3. Render a PDF with Playwright

This runnable Node.js example opens a page, waits for its load event, then saves a PDF. Playwright’s PDF API uses print CSS media. Set preferCSSPageSize when the page defines its own paper size with CSS; otherwise choose a paper format such as A4 or Letter.

import { chromium } from 'playwright';

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

Install the package and browser in the project, then run the script with a URL:

npm install playwright
npx playwright install chromium
node render.mjs https://example.com

Use the Page API documentation for current PDF options and the browser documentation to manage supported browser builds.

Wait for application readiness

The load event means the page load event fired; it does not guarantee that a single-page application has finished fetching or painting the content you need. If the page exposes a stable landmark, wait for it explicitly:

await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60_000 });
await page.locator('main article').waitFor({ state: 'visible', timeout: 20_000 });
await page.pdf({ path: 'page.pdf', format: 'A4', printBackground: true });

For pages that finish after asynchronous requests, a short bounded delay can help, but a selector tied to actual content is generally a clearer readiness condition. Avoid relying on network idle for pages that poll or maintain long-lived connections without checking whether that condition fits the site.

Useful PDF decisions

  • Print CSS: Playwright PDF generation uses print media. Use print styles for page breaks, margins, hidden navigation, and print-specific layouts.
  • Backgrounds: Set printBackground: true when background colors or images matter.
  • Paper size: Use a named format such as A4 or Letter, or use CSS page sizing with preferCSSPageSize.
  • Headers and footers: Consult the Page API for header/footer templates and their restrictions; check the resulting pagination in the installed browser version.
  • Screenshots: Use page.screenshot() when you need an image instead of a PDF. The Page API documents both output paths.

4. Render a PDF with wkhtmltopdf

For a public page, the basic command is:

wkhtmltopdf https://example.com page.pdf

For a local, controlled HTML file:

wkhtmltopdf ./report.html ./report.pdf

Use the exact options supported by your installed build. The command-line documentation includes JavaScript controls, delay and wait options, and page layout settings. Some features require a patched Qt build, so a switch appearing in the documentation does not guarantee it is available in every packaged binary. Check the wkhtmltopdf usage documentation.

JavaScript and delayed content

wkhtmltopdf can execute JavaScript in configurations that enable it. Its CLI provides controls to enable or disable JavaScript, add a delay, run scripts, and wait for a window status. These are useful for certain controlled pages, but they do not prove that a modern framework or a complex application will render correctly. Test the actual page and binary. A delay may also make every conversion slower without fixing content that never reaches the expected state.

5. Make a fair comparison

  1. Choose representative inputs. Include a static document, a JavaScript-rendered page, long content, images, and any page with the layout features your users need.
  2. Fix the environment. Record the OS or container image, renderer version, browser build, installed fonts, viewport, and network conditions.
  3. Define readiness. Decide which visible content must exist before capture. Use a selector or application signal where possible rather than assuming a load event means the page is finished.
  4. Compare output correctness. Check text, images, styles, page breaks, paper size, headers and footers, and missing content.
  5. Measure operations on your workload. Record duration, memory, failures, output size, and any setup or deployment burden. Repeat runs; do not infer a general speed winner from one page.
  6. Repeat after upgrades. Playwright’s browser versions move with its releases. Renderer updates, package changes, font changes, and deployment changes can alter output.

This is the reliable way to decide: no controlled head-to-head benchmark in the source material establishes a speed or fidelity winner for all websites.

6. Security, reliability, and cost

Security

The wkhtmltopdf project specifically warns against using the tool with untrusted HTML and recommends sanitizing user-supplied HTML and JavaScript. Do not pass arbitrary user content to a renderer as if it were a harmless document. Sanitize input and use an appropriate isolation boundary. For Playwright, assess browser workload isolation and current official security guidance against your own threat model; this comparison does not establish that browser automation is automatically safe for untrusted pages.

Reliability

  • Pin and record the renderer or Playwright version used to produce important documents.
  • Install the expected browser binaries as part of deployment, rather than relying on an undeclared system browser.
  • Set navigation and selector timeouts, catch failures, and close browser processes in cleanup code.
  • Validate fonts and external resources in the production environment; a local success does not ensure a container has the same fonts or network access.
  • Keep a small set of representative output fixtures and review them when changing engines or versions.

Cost and performance

There is no sourced universal runtime or cost comparison. Include browser installation and process memory, startup and concurrency behavior, conversion duration, retries, operational maintenance, and output correctness in your own cost estimate. A PDF generated faster but missing required content is not a successful conversion. For either option, bound concurrency and timeouts to match available resources, and measure representative documents on the deployment hardware.

7. Troubleshooting

Symptom Likely cause What to do
PDF is blank or missing app content Capture started before the app rendered, or a required request failed. Wait for a visible content selector or app-ready signal; inspect browser errors and network access. Do not assume load means application readiness.
CSS or layout differs from the live page PDF output uses print styles, or the engines implement different CSS behavior. Add or adjust print CSS; compare in the exact target engine and version. Use the same viewport, fonts, and page size when diagnosing.
Fonts or icons are missing Fonts are unavailable in the runtime or have not loaded before capture. Install required fonts in the deployment image, confirm font requests succeed, and wait for the page’s actual ready state.
Images are missing External resources are blocked, slow, or not loaded before output. Check resource URLs and network permissions. Wait for the relevant images or page state; test with the same network access as production.
wkhtmltopdf ignores an option The installed build may not include the required patched Qt feature. Check the binary’s version and build, then compare its supported options with the project usage documentation.
wkhtmltopdf JavaScript output is incomplete Scripts are disabled, a delay or wait condition is insufficient, or the page relies on behavior the older engine does not handle. Check JavaScript settings and page readiness, then test a representative page. If current browser compatibility is required, evaluate Playwright.
Playwright reports that an executable is missing The package is installed but its browser binary is not present in the runtime. Install the required browser for the deployed Playwright version with the Playwright install command, and make that installation part of deployment.
Navigation times out The site is slow, inaccessible, waiting indefinitely, or using activity incompatible with the chosen wait condition. Check URL access and network policy, set a deliberate timeout, and wait for the content selector you need rather than an unnecessarily strict global condition.
Output changes after deployment Renderer, browser, fonts, OS, or page data differ between environments. Record and align versions and fonts; reproduce with the production image and compare output fixtures.

8. Or skip the browser setup

If your task is to capture a website as an image rather than generate a PDF in your own renderer, ScreenshotNeo is a website screenshot API and MCP server. It returns PNG, JPEG, WebP, or PDF from one GET request. See the ScreenshotNeo API documentation for parameters and options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response reports the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for ScreenshotNeo and get 1,000 free screenshots a month, with no card required.

9. Frequently asked questions

Is wkhtmltopdf obsolete?

The official downloads page identifies 0.12.6 as the stable series there and dates it June 11, 2020. That dated statement is a reason to verify your exact build and needs; it does not establish that every existing use is broken.

Can Playwright render PDFs without showing a browser window?

Yes. The example launches Chromium in headless mode and calls the Page API’s PDF method.

Does a Playwright PDF look exactly like a screenshot?

No such guarantee follows from the API. PDF generation uses print CSS media, while screenshot capture represents a browser page image. Styles and pagination can differ.

Can I render user-submitted HTML?

The wkhtmltopdf project warns against untrusted HTML and recommends sanitizing supplied HTML and JavaScript. Treat renderer input as a security boundary and isolate workloads appropriately.

Which is faster?

There is no sourced head-to-head benchmark here. Measure your documents, deployment environment, and correctness requirements.