Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesUse Puppeteer when you need a PDF that matches a real web page. Install the package, launch its bundled browser, load a URL or HTML string, and call page.pdf(). Puppeteer supports modern CSS, JavaScript, web fonts, print media, page sizes, margins and background graphics because it renders the document in Chromium. The method below also shows how to control readiness, return PDF bytes from memory and diagnose the failures that make HTML-to-PDF jobs unreliable.
How do I convert HTML to PDF in Node.js?
The direct, general-purpose route is Puppeteer’s documented Page.pdf() API. Create a page, navigate to a URL (or set HTML), wait for the content your application actually needs, generate the PDF, then close the browser in a finally block. The API returns PDF bytes as a Uint8Array; passing a path writes the file for you, while omitting it lets you store the bytes in object storage or send them in an HTTP response.
Puppeteer’s guide displays version 25.12.0 at the time of writing. Package and browser requirements change, so check the current documentation before pinning a production image: Puppeteer PDF generation guide.
Install the package
npm install puppeteer
The regular package downloads a compatible Chromium during installation. In a container or CI environment, make sure the install step is allowed to download it and that the runtime has the libraries Chromium requires. If your organization supplies its own browser, configure Puppeteer for that executable and test the exact version you deploy.
#1 Best Overall
- INNOVATIVE CARTRIDGE-FREE PRINTING — No more dealing with lots of tiny ink cartridges; With this wireless document and photo printer each ink bottle set is equivalent to about 90 individual cartridges²
- LESS FREQUENT INK REPLACEMENT — Replacement ink bottles don't have to be changed nearly as often as ink cartridges¹; When you choose this combination printer, scanner and copier you can print up to 4,500 pages black/7,500 color³
- COLOR PRINTING — Up to 2 years of ink in the box4 (and with every replacement ink set) for fewer out-of-ink frustrations
- ZERO CARTRIDGE WASTE — By using an Epson EcoTank printer you can help reduce the amount of cartridge waste ending up in landfills
- HOME PRINTER DESIGNED FOR RELIABILITY — The Epson EcoTank ET-2800 All-in-One Supertank Color Printer creates vivid, detailed prints and documents thanks to Micro Piezo Heat-Free Technology; Fire off 10 ISO pages per minute1 to easily finish large jobs
Convert an HTML string to a PDF
import puppeteer from 'puppeteer';
const html = `<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
@page { size: A4; margin: 18mm; }
body { font-family: Arial, sans-serif; color: #222; }
h1 { break-after: avoid; }
.invoice-total { break-inside: avoid; }
</style>
</head>
<body>
<h1>Invoice 1042</h1>
<p>Rendered from an HTML template.</p>
<p class="invoice-total">Total: €240.00</p>
</body>
</html>`;
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.setContent(html, { waitUntil: 'networkidle0' });
const pdf = await page.pdf({
path: 'output.pdf',
format: 'A4',
printBackground: true
});
console.log(`Generated ${pdf.length} bytes`);
} finally {
await browser.close();
}
This is an adaptation of Puppeteer’s official pattern. The guide’s URL example uses waitUntil: 'networkidle2'; the string example above uses networkidle0. Neither is a universal definition of “ready.” A page with analytics, polling or a WebSocket may never become idle. Prefer an application-specific signal, such as a selector that appears after rendering, a short deliberate delay for a known animation, or a request you can await in your own code.
Convert a URL and save the file
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com/report', {
waitUntil: 'networkidle2',
timeout: 30_000
});
await page.pdf({
path: 'report.pdf',
format: 'A4',
printBackground: true,
margin: { top: '16mm', right: '14mm', bottom: '16mm', left: '14mm' }
});
} finally {
await browser.close();
}
page.goto() waits for navigation, not necessarily for data fetched after navigation. If the report is populated by client-side code, wait for a stable element (for example, await page.waitForSelector('[data-report-ready]')) before calling pdf(). Avoid adding --no-sandbox unless your deployment’s security model explicitly requires it; changing Chromium sandboxing has security consequences.
Rendering controls that change the result
The complete option reference is in Puppeteer’s Page.pdf() documentation and PDFOptions interface. These are the controls developers most often need.
| Option or call | What it controls | Important behavior |
|---|---|---|
format |
Paper preset such as A4 | If set, it takes priority over width and height. |
width, height |
Custom paper dimensions | Use when a preset does not match your output. |
landscape |
Orientation | Set true for wide reports. |
margin |
Top, right, bottom and left whitespace | Accepts CSS length strings. |
printBackground |
CSS backgrounds and graphics | Defaults to false; set true when color or background art matters. |
preferCSSPageSize |
Whether CSS @page size wins |
Useful for templates that define their own paper size. |
pageRanges |
Subset of pages | Generate selected ranges instead of the whole document. |
scale |
Overall rendering scale | Adjust carefully; it changes pagination and apparent font size. |
timeout |
PDF operation limit | Documented default is 30,000 ms. |
Print CSS versus screen CSS
page.pdf() uses the print CSS media type by default. If your design is defined under @media screen, select it first:
await page.emulateMediaType('screen');
await page.pdf({ path: 'screen-style.pdf', printBackground: true });
For normal print output, keep the default and define print-specific rules such as break-inside: avoid, break-before and an @page rule. Puppeteer modifies colors for printing by default. When exact colors are required, use CSS -webkit-print-color-adjust: exact; on the relevant elements, and still enable printBackground for backgrounds.
Rank #2
- CARTRIDGE-FREE PRINTING — Print lab-quality photos, graphics and creative projects; Get vibrant colors and sharp text with Epson's high-accuracy printhead and Claria ET Premium 6-color inks
- INK BOTTLES — Save on photos1 and creative projects with affordable in-house printing; All-in-one printer allows you to print 4" x 6" photos for about 4 cents each vs. 40 cents with traditional ink cartridges1
- LESS FREQUENT INK REPLACEMENT — Replacement ink bottles don't have to be changed nearly as often as ink cartridges¹; Printer, scanner and copier lets you print up to 6,200 color pages³
- PRINT FOR LONGER — Up to 2 years of ink in the box² (and with every replacement ink set) for fewer out-of-ink frustrations with this wireless printer
- ZERO CARTRIDGE WASTE — Epson EcoTank printer helps reduce the amount of cartridge waste ending up in landfills; Cartridge-free printer uses high-yield ink bottles; Each replacement ink bottle set is equivalent to about 100 individual ink cartridges⁴
Fonts, images and external assets
The current guide and API docs state that PDF generation waits for fonts by default. That does not guarantee that every remote image, stylesheet or API response has arrived. Serve assets from reachable URLs, use absolute URLs when setting an HTML fragment, and wait for a page-level ready condition after data and images are present. If you control the template, prefer self-hosted fonts and deterministic asset URLs so a transient third-party request cannot alter pagination.
Returning PDF bytes from an application
Do not write a temporary file when your framework can stream bytes directly. The value returned by page.pdf() is a Uint8Array in the current API. For an Express-style handler:
app.get('/invoice/:id.pdf', async (req, res, next) => {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
const html = await renderInvoice(req.params.id);
await page.setContent(html, { waitUntil: 'networkidle0' });
const pdf = await page.pdf({ format: 'A4', printBackground: true });
res.type('application/pdf').set('Content-Disposition', 'inline; filename="invoice.pdf"').send(Buffer.from(pdf));
} catch (error) {
next(error);
} finally {
await browser.close();
}
});
For high volume, avoid launching a new browser for every request. Keep a controlled browser process and create/close pages per job, cap concurrency, and recycle the process on a schedule appropriate to your workload. Measure memory and queue time in your own environment; the cited documentation does not establish a performance ranking.
Free tools Windows power users keep installed
One-click scans. No signup required.
Choosing an npm package: browser renderer or document API?
“HTML to PDF” packages are not interchangeable. A browser renderer interprets HTML, CSS and JavaScript. A document-generation library asks you to place text, images and drawing primitives through a PDF API.
| Route | HTML/CSS/JavaScript fidelity | Runtime model | Best fit |
|---|---|---|---|
| Puppeteer | High, because Chromium renders the page | Operates a real browser | Existing web templates, client-side charts and print CSS |
puppeteer-html-pdf |
Wrapper around a Puppeteer-style browser flow | Its npm listing shows configuration for A4 and a remote browser WebSocket endpoint | Teams wanting a wrapper; verify current activity, dependencies and Node compatibility |
html-pdf-node |
Listing accepts a URL or HTML content | Package-specific wrapper behavior | Simple integrations after reviewing its current API |
pdfkit |
Not an arbitrary HTML/CSS renderer | Programmatic PDF document generation | Precisely constructed documents where you control every drawing operation |
The npm listing for puppeteer-html-pdf reported version 4.0.8 and a publication age of about two years when checked; npm metadata is volatile, so recheck it before adoption. PDFKit’s listing showed version 0.20.2, published 22 days before that check. Those dates are observations, not guarantees of maintenance, security or speed.
Rank #3
- SET IT UP ONCE AND PRINT WITH CONFIDENCE. No complicated maintenance. Just easy, reliable printing you can count on.
- INK FOR YEARS. NOT MONTHS. Up to 2 years of ink included. Get thousands of pages of cartridge-free printing. More pages, less hassle
- KEEPS PRINTING WELL AFTER COMPETITORS HAVE QUIT. No complex maintenance. Sharper text, richer colors.[2] Only with HP Smart Tank
- PREMIUM SUPPORT - Strong technical expertise to solve issues faster
- THE LAST PRINTER YOU'LL EVER NEED. Enjoy years of refillable, cartridge-free printing.
Common failures and fixes
“Could not find Chrome” or launch failure
- Run
npm installin the deployment image so Puppeteer’s browser download occurs. - Check that the container includes Chromium’s required system libraries and writable temporary storage.
- If using a separately installed browser, configure its executable path and test the exact binary in CI.
PDF is blank or missing late content
- Navigation completed before client-side rendering. Wait for a content-specific selector or application promise.
- A protected page returned a bot check or login screen. Authenticate with a controlled session and verify the final URL and title before generating.
- Remote fonts or images failed. Inspect network errors, use absolute URLs and ensure the browser can reach those hosts.
Colors, backgrounds or layout differ from the website
- Remember that print media is the default; call
emulateMediaType('screen')only when screen rules are wanted. - Set
printBackground: trueand, for exact color reproduction, apply-webkit-print-color-adjust: exact. - Check margins,
@page,preferCSSPageSizeand scale together. A paper-size mismatch can move an element to the next page.
Timeouts and hanging jobs
- Set an explicit navigation and PDF timeout appropriate to your content, rather than assuming the 30-second default fits every report.
- Do not use network-idle as a blanket rule for pages with polling or analytics; wait for a deterministic selector.
- Always close pages and browsers in
finallyblocks, and enforce an outer job deadline so a failed request cannot consume a worker indefinitely.
Broken page breaks
- Add print CSS such as
break-inside: avoidto cards, tables and totals. - Use
break-beforeorbreak-afterfor intentional section boundaries. - Reduce oversized margins or scale only after confirming the CSS page size and paper format are correct.
Or skip the browser setup
ScreenshotNeo provides a hosted website screenshot and PDF API when you do not want to install or operate Chromium. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks, 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. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.
For a PDF capture, use the API endpoint and request PDF output as documented at ScreenshotNeo’s API documentation:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
curl -G "https://api.screenshotneo.com/v1/shot"
-d access_key=YOUR_API_KEY
--data-urlencode url=https://example.com/report
-d format=pdf
-o report.pdf
The same endpoint supports full-page captures, CSS-selector elements, custom CSS and JavaScript, waits, headers, cookies, user agents, authorization, timezone, geolocation, page ranges, paper size, margins and signed webhooks for asynchronous jobs. It also offers bulk capture for up to 100 URLs per call and a usage API. Plans include 1,000 shots per month free with no card; paid plans start at $5 for 3,000 shots, and every feature is on every plan. Create a free ScreenshotNeo account.
FAQ
Can Puppeteer convert an HTML file on disk?
Yes. Read the file, pass its contents to page.setContent(), and ensure relative assets resolve by using absolute URLs or a suitable document base URL. For sensitive files, avoid exposing them through a publicly reachable web server.
Should I use a URL or an HTML string?
Use URL navigation when your application already serves the page and its assets. Use setContent() for generated templates or isolated documents. In both cases, define an explicit readiness condition for dynamic content.
Does Puppeteer’s PDF API support CSS-defined paper sizes?
Yes. Define @page in your stylesheet and use preferCSSPageSize: true when that CSS size should take priority over the JavaScript options.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Rank #4
- Wireless Bluetooth Printer: Portable thermal printer compatible with iPhone, Android phones, iPad and tablet computers via Bluetooth. For smartphones, please download the "Nada Print" App. You can also connect to laptops and computers for printing using a USB-C cable. (Note: Laptops and computers can only be connected via USB and require the installation of a driver first. Bluetooth connection is not supported.)
- No-ink printing: Only supports US Letter and A4 size thermal paper.(Doesn't support regular paper) The no-ink portable thermal printer uses direct thermal technology, requiring no ink, toner or ribbons, making it environmentally friendly, cost-effective and time-saving. The thermal printer package comes with a roll of US Letter thermal printing paper. Note: When installing the paper, remember to switch the paper size switch on APP
- Clear Print: NDYIN N80 portable thermal printer adopts high-definition printing technology, with a 203DPI resolution to provide you with clear printing results. This mobile printer is compatible with roll paper, folded paper and tattoo transfer paper, supporting printing from your mobile phone PDF, Word, pictures and web pages anytime and anywhere. It is recommended to use our NDYIN thermal paper to achieve good printing quality
- Portable wireless printer for travel: The thermal printer is equipped with a built-in 1500mAh rechargeable battery, which can print 160 sheets of 8.5" x 11" thermal paper after being fully charged. It weighs only 1.5 pounds and is compact in size. This ink-free portable printer can be easily carried in a backpack or briefcase! It is perfect for business travel, cars, small offices, construction sites, schools and homes. You can print documents, contracts, invoices and boarding passes anytime and anywhere
- The N80 thermal printer has a wide range of uses. The package includes the N80 printer, a roll of US Letter paper(7m/roll), a user manual, a guide card, a type-C soft cable and a type C adapter. Note: The charging adapter is not included. Special thermal paper is required for use; ordinary paper cannot be used. This ink-free portable thermal printer is suitable for various scenarios such as home, school, travel, office, and outdoor, meeting the printing needs of different groups of people. This tattoo template printer is also compatible with tattoo transfer paper, making it an ideal choice for tattoo art
Is PDFKit a drop-in replacement for HTML-to-PDF conversion?
No. Its npm description presents it as a JavaScript PDF document-generation library. Choose it when you want to construct the document programmatically, not when you need arbitrary website HTML and CSS rendered as a browser would.
Frequently Asked Questions
Can Puppeteer convert an HTML file on disk?
Yes. Read the file, pass its contents to page.setContent(), and use absolute URLs or a suitable base URL for relative assets.
Should I use a URL or an HTML string?
Use URL navigation for an existing served page and setContent() for generated templates; either way, wait for a deterministic readiness condition.
Does Puppeteer support CSS-defined paper sizes?
Yes. Define @page and set preferCSSPageSize: true when the stylesheet should control paper dimensions.
Is PDFKit a drop-in HTML-to-PDF replacement?
No. PDFKit is a programmatic document-generation library rather than an arbitrary HTML/CSS browser renderer.
Quick Recap
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.




