The right way to convert HTML to PDF in JavaScript depends on where the code runs. For a user-initiated save, use the browser’s print flow and print CSS. For a browser-side download of an element, html2pdf.js is a convenience option, but it creates image-based PDFs. For server-side rendering in Node.js, use Puppeteer’s page.pdf(). Use pdf-lib when you need to create or edit PDF objects directly, not to render arbitrary HTML and CSS.
Choose an approach based on runtime and output
| Approach | Where it runs | Use it for | Main trade-off |
|---|---|---|---|
| Browser print flow | In the user’s browser | Letting users print or save the page through the browser’s print controls | The browser controls the final dialog and settings; you need a print layout that works in supported browsers. |
| html2pdf.js | Browser only | A simple client-side PDF download of an element or page | It renders content as an image, so PDF text is not selectable or searchable; reflow and canvas-size limits can affect results. |
Puppeteer page.pdf() |
Node.js-controlled browser workflow | Automated or server-side PDF generation from a rendered page | PDF output uses print media by default, so print styling, assets, colors, and browser lifecycle need attention. |
| pdf-lib | Browser, Node.js, Deno, React Native, and other supported JavaScript runtimes | Creating or modifying PDF documents, pages, forms, text, and images | It is not a browser layout engine for converting arbitrary HTML/CSS. |
These tools solve different problems; choose by where HTML is rendered and whether you need browser layout or direct PDF editing. The html2pdf.js project documents its browser-only workflow and dependencies in its README. Puppeteer documents PDF behavior in its Page.pdf() API reference and PDF generation guide. pdf-lib describes its creation and modification capabilities in its project documentation.
Let users print or save the rendered page
If the PDF should reflect a page the user is already viewing, call window.print() in response to an explicit user action. The browser presents its print interface, where the user chooses a printer or a PDF destination and controls available print settings. Add a print stylesheet to remove screen-only controls and tune page layout.
<button type="button" onclick="window.print()">Print or save as PDF</button>
<style>
@media print {
.site-nav,
.print-button,
.screen-only {
display: none !important;
}
main {
width: auto;
margin: 0;
}
h1, h2, h3 {
break-after: avoid;
}
.new-page {
break-before: page;
}
}
</style>
Use semantic, print-friendly HTML and check the page in each browser you support. Page breaks, margins, backgrounds, and available dialog options may differ by browser and user settings. Keep essential content visible in print rather than relying on navigation or interactive controls.
#1 Best Overall
Download a browser-side PDF with html2pdf.js
html2pdf.js is a browser-side pipeline built on html2canvas and jsPDF. Install it with npm using npm install --save html2pdf.js, then pass the target element to the library. Its project documentation says it must run in a browser, not Node.js.
import html2pdf from 'html2pdf.js';
const element = document.getElementById('element-to-print');
if (!element) {
throw new Error('Could not find #element-to-print');
}
html2pdf().from(element).save('page.pdf');
The short pattern documented by the project is html2pdf(element); the chained form above makes the source element and save action explicit. Make sure the element exists and that its fonts and images have loaded before starting the capture. Try the result with representative content, including long pages and complex styling, in the browser where the feature will run.
Understand the image-based output
The project’s documented renderer creates an image and places it in the PDF. As a result, the text is not selectable or searchable like text in a document rendered by a browser’s print-to-PDF engine. The renderer may resize or reflow content to fit pages, and very large canvas dimensions can exceed browser limits and yield blank output. The README also identifies HTML rendering and cloned-node/CSS issues as known limitations. Check web fonts, images, page breaks, and styling with your actual page before relying on the output.
Rank #2
Generate a PDF from Node.js with Puppeteer
Puppeteer drives a browser and exposes page.pdf() to generate a PDF from the loaded page. Install Puppeteer in your Node.js project, then launch a browser, navigate to the target, generate the file, and close the browser. The following complete example closes the browser even if navigation or PDF generation fails:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
const puppeteer = require('puppeteer');
async function savePageAsPdf(url, outputPath = 'output.pdf') {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle2' });
await page.pdf({ path: outputPath });
} finally {
await browser.close();
}
}
savePageAsPdf('https://example.com', 'output.pdf').catch((error) => {
console.error('PDF generation failed:', error);
process.exitCode = 1;
});
This follows the lifecycle in Puppeteer’s PDF generation guide. Its documented page.pdf() behavior uses print CSS media by default and waits for fonts by default. That makes print styles—not necessarily the page’s on-screen appearance—the starting point for the PDF.
Choose print or screen media deliberately
If you want the page styled for screen rather than print, emulate screen media before generating the PDF:
await page.emulateMediaType('screen');
await page.pdf({ path: 'output.pdf' });
The API documentation also notes that printing modifies colors by default. If exact color rendering is important, use the CSS print-color adjustment property it documents, such as -webkit-print-color-adjust: exact, and verify the output in your target environment. Background graphics and other print settings can affect the final appearance.
Wait for the content your page needs
The guide’s example navigates with waitUntil: 'networkidle2'; that is a navigation condition, not proof that every application-specific task or delayed asset is finished. If your page renders data asynchronously, wait for a selector or application-ready signal before calling page.pdf(). Confirm that images and fonts are available, and consider pages that load content only after scrolling or interaction. Puppeteer’s documented default font waiting helps with fonts, but does not replace checks for the rest of your page’s readiness conditions.
Use pdf-lib to build or edit PDF documents
Choose pdf-lib when the output is assembled from PDF primitives or when you need to modify an existing PDF: add pages, draw text or images, embed fonts, fill forms, or split and merge documents. The project documents support across several JavaScript environments and examples using PDFDocument.create() and page drawing methods.
Rank #4
It is not a drop-in replacement for Chromium when the input is a complex HTML/CSS layout. If visual fidelity to a webpage matters, render the page with a browser workflow such as Puppeteer; if you need precise control over PDF objects, work directly with a PDF library such as pdf-lib.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
For a one-request screenshot or PDF of a URL, ScreenshotNeo provides a website screenshot API and MCP server. Its GET endpoint can return an image or PDF; the PDF example is below. See the API documentation for options and authentication.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.pdf
- Cookie and consent banners, newsletter popups, and chat widgets are removed before capture; each cleanup step can be turned off.
- Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; the response reports the page verdict and billing status in headers.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for Claude, Cursor, and other MCP clients. - The free plan includes 1,000 shots per month with no card required; paid plans start at $5 for 3,000 shots.
Sign up free for 1,000 screenshots a month, with no card.
Recommended Free Tools
Best Value
Troubleshoot common HTML-to-PDF problems
- html2pdf.js fails in Node.js: it is documented as browser-only. Use Puppeteer for a Node.js-controlled browser workflow instead.
- The PDF text cannot be selected or searched: html2pdf.js places a rendered image in the PDF. Use browser printing or Puppeteer’s browser-generated PDF when text output matters.
- The html2pdf.js output is blank or clipped: oversized canvas dimensions can hit browser limits. Test a smaller or shorter capture and review the project’s documented HTML and cloned-node/CSS limitations.
- Puppeteer output uses the wrong layout:
page.pdf()defaults to print media. Add or fix@media printstyles, or callpage.emulateMediaType('screen')before PDF generation if screen styling is intended. - Fonts or images are missing: ensure assets are reachable and the application has finished rendering before generating the PDF. Puppeteer waits for fonts by default, but delayed data or images may need an application-specific readiness check.
- Colors differ in Puppeteer output: PDF printing adjusts colors by default. Use the documented print-color adjustment CSS when exact colors are required, then verify the result.
- The browser remains open after an error: put
browser.close()in afinallyblock so navigation or PDF failures do not skip cleanup.
Frequently asked questions
Can browser JavaScript save a PDF without opening a print dialog?
The print-flow approach hands the final destination and settings to the browser’s print interface. For a direct browser-side element download, html2pdf.js provides a download workflow, with the image-based output limitations described above.
Should I use pdf-lib or Puppeteer for an HTML invoice?
Use Puppeteer if the invoice already exists as HTML/CSS and you want browser layout rendering. Use pdf-lib if you are assembling the document from PDF-level text, images, forms, or pages rather than rendering an HTML layout.
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.




