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.

You can avoid a headless browser in Node.js, but the right method depends on what “convert HTML to PDF” means for your project. For a layout you can build directly with JavaScript, use PDFKit to create the PDF without HTML. For existing HTML, a non-browser renderer such as html-pdf-lite can accept markup, but its CSS support is not equivalent to Chromium. If you need a PDF of a page that is already hosted, a hosted screenshot API is another option; that sends the rendering work to an external service rather than rendering your HTML string locally.

Choose the method that matches your input

“Without a headless browser” rules out tools that launch Chromium or another browser engine. It does not mean every alternative can faithfully print any website. The key choice is whether you need to preserve HTML or only produce a PDF with the same information.

Approach Input and output Best fit Main trade-off
PDFKit JavaScript document operations produce a PDF stream Invoices, receipts, reports, and layouts you can construct as PDF elements You recreate the layout in PDFKit; it is not an HTML/CSS renderer. PDFKit project
html-pdf-lite HTML string produces a PDF Buffer Controlled templates where its supported layout and styling are sufficient It is not a full Chromium renderer; complex layout and browser CSS fidelity are limited. Project repository
html-to-pdfmake with pdfmake HTML is converted to a pdfmake document definition, which is then used to generate a PDF A constrained HTML subset that maps to pdfmake’s document model It does not promise arbitrary web-page rendering. Check current supported tags and styles in the package and pdfmake documentation. Package page
Hosted HTML-to-PDF API Markup is sent over HTTP and PDF bytes are returned by an external service Teams that prefer a service boundary instead of installing a renderer locally Rendering depends on a network service and its current terms, availability, data handling, and pricing. pdfkitt Node.js page

If you have an existing page whose appearance depends on modern CSS, first render representative documents with your candidate non-browser renderer. Test the elements that matter to your output: page breaks, fonts, images, tables, and layout. If browser-level fidelity is a hard requirement, the no-headless-browser constraint may be incompatible with the document.

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

Generate a PDF directly with PDFKit

PDFKit is the simplest fit when your content is structured data and you can define the document layout yourself. It produces PDFs through a document API and Node streams; it does not parse an HTML input document. The official getting-started guide documents installation, creating a PDFDocument, piping it to a writable stream, and calling end() to finish the file. See PDFKit’s getting-started guide.

Install

In an existing Node.js project, install the package:

npm install pdfkit

The following ES module example creates a small PDF file. Use an environment configured for ES modules, or adapt the import to the CommonJS form documented by PDFKit.

import fs from 'node:fs';
import { PDFDocument } from 'pdfkit';

const doc = new PDFDocument();
const output = fs.createWriteStream('output.pdf');

doc.pipe(output);
doc.fontSize(18).text('Generated directly as a PDF');
doc.end();

await new Promise((resolve, reject) => {
  output.on('finish', resolve);
  output.on('error', reject);
});

Waiting for the writable stream’s finish event makes the example’s completion explicit before subsequent code treats the file as ready. Build the page by adding the text, images, and drawing operations your application needs. If your current source is HTML, PDFKit will not convert its tags or CSS for you: you must map the content and layout into PDFKit operations.

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

When this route is a poor fit

  • Your source is a complex HTML page and maintaining a second, manually recreated layout would be costly.
  • The output must closely match browser rendering, including CSS-driven layout behavior that you have not recreated.
  • Your application needs to accept arbitrary user HTML and render it unchanged.

Render an HTML template without Chromium

html-pdf-lite is a project-specific option for passing an HTML string to a non-browser renderer built on PDFKit. Its repository documents renderPdfFromHtml(html, options) returning a Buffer, so you can write those bytes to disk or handle them in memory. It is not a general Chromium substitute: the maintainers describe complex flexbox and grid support as partial and warn that browser CSS fidelity is not guaranteed. Check the repository’s current API and limitations before adopting it.

Install and run

npm install html-pdf-lite
import fs from 'node:fs/promises';
import { renderPdfFromHtml } from 'html-pdf-lite';

const html = '<h1>Invoice</h1><p>Amount due: $42</p>';
const pdf = await renderPdfFromHtml(html);

await fs.writeFile('invoice.pdf', pdf);
console.log('Wrote invoice.pdf');

This example uses a deliberately small HTML string. Start with a representative version of your actual template and verify the resulting PDF before putting the renderer into production. In particular, inspect pagination, fonts, images, tables, and any CSS features on which your layout depends. If a feature is unsupported or rendered differently, simplify the template, choose a different PDF-generation approach, or revisit the requirement to avoid a browser.

Scripts and untrusted markup

The project documentation says scripts are disabled by default and labels script execution unsafe. It warns against rendering untrusted HTML; enabling allowScripts can execute embedded scripts in the process. Keep scripts disabled unless you have a specific, reviewed need, and do not pass user-controlled markup to the renderer without an appropriate security design. Read the project’s security notes.

For either PDFKit or an HTML-accepting renderer, treat filesystem paths, fonts, and image sources as application inputs that need validation. PDFKit’s guide describes Node builds as having filesystem access and Node streams; it is not a security review of your particular deployment. PDFKit getting-started documentation.

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

Use html-to-pdfmake for a constrained document model

html-to-pdfmake offers a different bridge: convert HTML into a pdfmake document definition, then use pdfmake to generate the PDF. That can suit a constrained template whose tags and styles map to the document-definition model. It should not be treated as a way to print arbitrary web pages without checking support. The package page points readers to pdfmake documentation for supported features; consult those current references and test the precise markup and styles you plan to use. Package information.

Use a hosted API when the HTML is available to a service

A hosted HTML-to-PDF API can keep browser installation and rendering out of your Node deployment, but it introduces an external dependency. pdfkitt’s Node.js page describes sending HTML in an HTTP request and receiving PDF bytes. That is vendor-described behavior, not an independent evaluation; check the provider’s current terms, pricing, availability, limits, and data-handling practices before sending document content. pdfkitt Node.js page.

A hosted page-capture API is a related but distinct case: it captures a URL, not necessarily an arbitrary HTML string supplied by your application. For a private or dynamically generated document, confirm that the service can access the page and that its PDF options fit the result you need. Do not assume that a URL-capture API is a drop-in replacement for a local HTML-string renderer.

Performance, reliability, and cost

Do not generalize a package benchmark

The html-pdf-lite maintainers report a cold-start comparison of 86 ms for html-pdf-lite and 654 ms for Puppeteer in their repository. They describe the benchmark as run on Node 22, with A4 output and 15 warm iterations. These are project-reported measurements, not an independent benchmark, and they do not predict performance for your templates, hardware, or deployment. The same repository reports separate warmed results by sample template; those figures need their template and setup context to be meaningful. Benchmark details from the maintainers.

Free tools Windows power users keep installed

One-click scans. No signup required.

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

Measure the workload you will deploy

  • Measure generation time for representative documents, including the largest templates you expect to process.
  • Check memory use and concurrent jobs under your own runtime and deployment limits.
  • Include assets such as fonts and images in tests; their availability and loading behavior can affect the finished document.
  • Compare the operational burden of a local package with the network, data-handling, and service-availability dependency of a hosted API.

These are evaluation steps, not claims that one renderer is faster or more reliable for every workload. Package versions, API behavior, and vendor offers can change, so verify them when selecting a production dependency.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot common conversion problems

The PDF is missing or empty

With PDFKit, confirm that the document is piped to a writable stream and that doc.end() is called. In an application, handle stream errors and wait for the output stream to finish before reading or serving the file. With a Buffer-returning HTML renderer, confirm that the promise resolves and that the resulting bytes are written to the intended destination.

HTML appears, but the layout differs

This is a renderer capability issue, not necessarily a failed conversion. Reduce the template to a small reproduction, then check whether its layout depends on complex flexbox, grid, or other browser-specific behavior. Adjust the markup and CSS to the renderer’s supported subset, use direct PDF APIs for a controlled layout, or choose a browser renderer if fidelity is essential.

Page breaks, tables, or fonts look wrong

Use a production-like sample rather than relying on a single heading-and-paragraph test. Check how the renderer handles each specific feature, including the fonts and images your application supplies. If the output is unacceptable after adapting the template, the chosen non-browser renderer may not fit the requirement.

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

Rendering user-supplied HTML creates risk

Do not enable script execution casually. The html-pdf-lite maintainers warn against untrusted HTML and say script execution is unsafe. Keep scripts disabled by default and review or sanitize input according to your application’s threat model; sanitization alone should not be treated as proof that arbitrary markup is safe.

A hosted conversion request fails

Check the provider’s current request format, response handling, limits, and service status in its own documentation. Also verify that the service is permitted to receive the document and can access any URL or assets it needs. A network service adds failure modes that do not exist in a purely local PDF-generation flow.

Or skip the browser setup

If the page is published at a URL and a hosted capture suits your use case, ScreenshotNeo returns screenshots or PDFs from one GET request. This is URL capture, not a local conversion of an arbitrary HTML string. Its capture flow accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; those steps can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in headers. It also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for AI agents. Learn about ScreenshotNeo.

For PDF output, set the output format using the current API documentation. The following Node.js request follows the provided API example and targets a hosted page:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Check the response status and use the documented output-format options and response handling for your application; do not treat a URL capture as a way to submit a raw HTML string. See the ScreenshotNeo API documentation for the request details. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Sign up for 1,000 free screenshots a month with no card.

Make the decision with a representative document

For a controlled report or invoice you can lay out yourself, PDFKit avoids HTML rendering entirely. For existing HTML, try a non-browser renderer only after checking its output against production-like templates and its supported CSS. For an HTML page already hosted at a URL, a hosted API can remove local renderer setup, provided its external-service trade-offs and access model suit the content. The decisive test is not whether a library accepts HTML; it is whether the resulting PDF meets the layout, security, deployment, and data-handling requirements of your application.

Frequently Asked Questions

Does PDFKit convert an HTML string to PDF?

No. PDFKit creates a PDF through JavaScript document operations; it does not parse HTML or CSS.

Can a URL screenshot API convert private, unsaved HTML?

Not by URL alone. A URL capture service needs a page it can access; it is distinct from passing an arbitrary HTML string to a local renderer.

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

Is html-pdf-lite pixel-perfect with Chrome?

No. Its maintainers say it is not a full Chromium renderer and browser CSS fidelity is not guaranteed.

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.