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 html-pdf-node‘s generatePdfs(files, options) method. Pass an array containing HTML content strings or public url values, await the returned promise, then write each returned PDF buffer to a filename you control. One shared options object sets paper size, margins, CSS page sizing, page ranges, orientation, backgrounds and Chromium flags for the whole batch.

Install html-pdf-node and prepare the output directory

Install the package in a Node.js project:

npm install html-pdf-node

The package wraps a Chromium-based renderer. Your deployment therefore needs a compatible browser installation and permission to launch it. If you convert URLs, the renderer also needs network access to those pages. The package README documents version 1.0.8 on npm’s 2026 page; package metadata and download counts can change, so verify them before locking a production dependency.

mkdir -p out

Use stable names in your input records. The name property is an application-level identifier that lets you match each returned buffer to its source; it is not a promise that the library will write the file for you.

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

Generate a batch from HTML strings and URLs

This complete CommonJS example creates two invoices from inline HTML and one report from a public URL. It passes one options object to every document, checks that the result count matches the input count, and writes each buffer independently.

const htmlToPdf = require('html-pdf-node');
const fs = require('node:fs/promises');

const files = [
  {
    content: `<!doctype html>
      <html><head><meta charset="utf-8">
      <style>body{font-family:Arial,sans-serif} h1{color:#174a7e}</style>
      </head><body><h1>Invoice 1001</h1><p>Alice</p></body></html>`,
    name: 'invoice-1001.pdf'
  },
  {
    content: '<h1>Invoice 1002</h1><p>Bob</p>',
    name: 'invoice-1002.pdf'
  },
  {
    url: 'https://example.com/report',
    name: 'report.pdf'
  }
];

const options = {
  format: 'A4',
  printBackground: true,
  margin: { top: '12mm', right: '12mm', bottom: '12mm', left: '12mm' },
  path: false
};

async function run() {
  await fs.mkdir('./out', { recursive: true });
  const results = await htmlToPdf.generatePdfs(files, options);

  if (results.length !== files.length) {
    throw new Error(`Expected ${files.length} PDFs, received ${results.length}`);
  }

  await Promise.all(results.map(async ({ name, buffer }, index) => {
    if (!Buffer.isBuffer(buffer)) {
      throw new TypeError(`Result ${index} did not contain a PDF buffer`);
    }
    const safeName = files[index].name || name;
    await fs.writeFile(`./out/${safeName}`, buffer);
  }));

  console.log(`Wrote ${results.length} PDFs to ./out`);
}

run().catch(error => {
  console.error(error);
  process.exitCode = 1;
});

The repository documents generatePdfs(files, options) as a promise that resolves to an array of objects containing PDF buffers. In practice, keep the input order and your own IDs so a result can never be assigned to the wrong customer or report. See the html-pdf-node README for the documented API.

Understand the input and returned output

HTML content entries

Use { content: '<h1>…</h1>', name: 'file.pdf' } when your application already rendered a document. Include a complete HTML document when you need a <head>, embedded fonts, print styles or page rules. Inline assets avoid a second network dependency; external assets must be reachable by Chromium.

URL entries

Use { url: 'https://your-site.example/report', name: 'report.pdf' } for a publicly reachable page. Authentication, robots rules, slow APIs and client-side rendering can affect the result. The README documents URL input but does not publish a wait strategy or timeout contract, so make the page deterministic before calling the batch API and test slow pages in your own environment.

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

Buffer persistence

Each returned object includes a PDF buffer. Save it with fs.writeFile, stream it to object storage, or send it in an HTTP response. The explicit buffer workflow gives you per-file validation and naming. The path option is documented as the file-path setting, but returned buffers are the clearer choice when every input needs a distinct destination and error handling.

Set paper size, margins and page selection

All documents in a call share the options object. The main controls are:

Option Use Important behavior
format Named paper such as 'A4' The README says the default is Letter. Set A4 explicitly when that is your required standard.
width, height Custom paper dimensions Supply units such as 210mm or 8.5in. A set format takes priority over these dimensions.
margin Printable whitespace Set top, right, bottom and left, each with a unit.
pageRanges Select pages Examples include 1-5, 8, 11-13. An empty value means all pages.
preferCSSPageSize Honor CSS page rules When true, CSS @page size takes priority over width, height or format.
printBackground Include background graphics The documented default is false; set true for colored panels, backgrounds and many charts.
landscape Rotate orientation The documented default is false (portrait).

A4 with CSS-controlled page size

const options = {
  format: 'A4',
  preferCSSPageSize: true,
  printBackground: true,
  margin: { top: '15mm', right: '15mm', bottom: '15mm', left: '15mm' }
};

With preferCSSPageSize enabled, put rules such as @page { size: A4 landscape; margin: 12mm; } in the document’s stylesheet. Without it, the JavaScript paper settings control the page.

Custom dimensions and page ranges

const options = {
  width: '210mm',
  height: '297mm',
  pageRanges: '1-5, 8, 11-13',
  printBackground: true
};

Do not set a conflicting format when custom dimensions must win; the documented precedence gives format priority. Page ranges are applied to each generated PDF, not to the batch as a whole.

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.

Control Chromium arguments safely

The args option passes additional Chromium flags. The README shows --no-sandbox and --disable-setuid-sandbox as defaults:

const options = {
  format: 'A4',
  args: ['--no-sandbox', '--disable-setuid-sandbox']
};

Changing sandbox flags changes the browser’s security posture. Review the container, operating-system user and deployment policy before removing or adding flags. A local success does not prove that a locked-down production container can launch Chromium.

Build a reliable batch pipeline

  1. Render each source. Produce deterministic HTML or verify that every URL is reachable from the worker.
  2. Assign a stable name. Keep a database ID separate from the filename and sanitize names before writing to disk.
  3. Use shared options. Put paper, margin and browser settings in one object so every output follows the same print policy.
  4. Await the promise. Do not read a buffer before generatePdfs resolves.
  5. Validate cardinality and type. Confirm one result per input and that every result has a buffer.
  6. Persist with isolation. Write each file to a controlled directory or object-storage key; record failures with the input ID.

The package publishes no throughput or concurrency benchmark. Measure rendering time, memory use, browser startup cost and failure rate with your real templates. For large workloads, queue jobs, cap concurrent batches according to available CPU and memory, and retry only transient URL or infrastructure failures. Retrying malformed HTML or an inaccessible page will not fix the cause.

Troubleshoot common failures

“Cannot find module ‘html-pdf-node’”

Run npm install html-pdf-node in the project that executes the script, then check that the same Node.js environment runs it. A global install does not satisfy a local dependency reliably.

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.

Chromium fails to launch in a container

Confirm that the package’s browser dependency is installed and executable by the service user. Review the documented sandbox arguments and your container security policy; do not blindly disable protections in a multi-tenant environment.

The PDF is blank or missing styles

For inline content, include a valid HTML document and embed or expose required assets. Set printBackground: true for backgrounds. For URLs, test the page from the same network, wait until server-rendered content is available, and avoid relying on an undocumented readiness timeout.

Remote images or fonts do not appear

Check HTTPS access, DNS, certificate validity and cross-origin restrictions from the renderer. Prefer absolute URLs or inline assets, and ensure the URL is not protected by an interactive login.

Output names are wrong or files are overwritten

Do not use a shared literal filename. Give every input a unique name, map results by index or an external ID, sanitize path separators, and write to a directory created for the job.

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

Only selected pages are present

Inspect pageRanges. An empty value means all pages; a range such as 1-5, 8, 11-13 intentionally omits other pages.

A custom size is ignored

Remove format when using width and height, or set preferCSSPageSize: true if the CSS @page rule should control the size.

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

Or skip the browser setup

ScreenshotNeo provides a website screenshot and PDF API when your input is a URL rather than locally generated HTML. One GET request returns a PNG, JPEG, WebP or PDF. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.

Read the complete parameter list in the ScreenshotNeo API documentation. The one-call example below targets a PDF-producing endpoint; change the URL to your page and use the returned file as needed:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

For applications that need explicit PDF output, request the documented PDF option for your endpoint configuration. The service also supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets and arbitrary viewports, retina scale, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, chosen-TTL caching, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Parameter names used by other screenshot APIs are accepted to ease migration.

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
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}`);
if (!res.ok) throw new Error(`ScreenshotNeo HTTP ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());

ScreenshotNeo’s Free plan includes 1,000 screenshots per month without a card. Paid plans start at $5 for 3,000 shots; every feature is on every plan, and yearly billing gives two months free. Create a free ScreenshotNeo account to try the URL workflow.

FAQ

Does generatePdfs write files automatically?

It resolves with objects containing PDF buffers. Your application decides filenames and storage; writing the buffers explicitly is the predictable batch pattern.

Can one batch mix HTML and URLs?

Yes. Each array entry can provide either a public url or an HTML content string, so the example can combine both sources.

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

Is there an official throughput limit?

The package documentation publishes no throughput or concurrency benchmark. Establish capacity with representative documents and your own browser-hosting environment.

Frequently Asked Questions

Does generatePdfs write files automatically?

It resolves with objects containing PDF buffers. Your application decides filenames and storage; writing the buffers explicitly is the predictable batch pattern.

Can one batch mix HTML and URLs?

Yes. Each array entry can provide either a public url or an HTML content string, so a batch can combine both sources.

Is there an official throughput limit?

The package documentation publishes no throughput or concurrency benchmark; measure capacity with representative documents in your own environment.

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

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.