Blog post

Convert HTML to PDF with JavaScript: Top libraries compared

Do you need to generate a PDF from HTML in your JavaScript app? You’ve got options — but not all of them are built the same. This guide compares three top JavaScript tools — html2pdf, pdfmake, and Playwright — explaining how each handles HTML-to-PDF conversion, their pros and cons, and which library is best suited for client-side, structured, or high-fidelity PDF generation.

Illustration: Convert HTML to PDF with JavaScript: Top libraries compared

TL;DR:

If you need a quick way to generate PDFs in the browser, html2pdf.js is simple to use but limited to static, image-based output. For fully customizable PDFs built from JavaScript data structures, pdfmake gives you control without relying on HTML. For high-fidelity PDFs rendered from actual HTML and CSS, Playwright offers the most accurate output, though it requires a server-side setup.

So, how do these tools actually work in practice? This post will begin with the simplest option: html2pdf.js.

1. html2pdf.js — Simple client-side PDF generation

The html2pdf library converts HTML pages to PDFs in the browser. It uses html2canvas and jsPDF under the hood. html2canvas renders an HTML page into a canvas element and turns it into a static image. jsPDF then takes the image and converts it to a PDF file.

Check out our blog on how to convert HTML to PDF using React for a step-by-step guide on how to use jsPDF in a React app.

Installation methods (CDN, npm, and manual download)

You can add html2pdf.js in three ways:

  • Using the CDN link for html2pdf

The library provides a CDN link. If you prefer this method, add a script tag to your HTML file:

<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js" integrity="sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzZ8zBps+dvLusV+eNQATqgA/HdeKFVgA5v3S/cIrLF7QnIg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  • Installing via npm

npm install html2pdf.js
  • Downloading the library manually

Get the bundle from GitHub. After you’ve downloaded the library, add a script tag to your HTML file:

<script src="html2pdf.bundle.min.js"></script>

Convert HTML to PDF using html2pdf

To begin, define a generatePDF() function that will get the element you want to download as a PDF from your HTML files. Then, call html2pdf with that element to download it directly on your users’ client. Next, call the following function in a download button:

<!DOCTYPE html>
<html>
	<head>
		<!-- html2pdf CDN link -->
		<script
			src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"
			integrity="sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzZ8zBps+dvLusV+eNQATqgA/HdeKFVgA5v3S/cIrLF7QnIg=="
			crossorigin="anonymous"
			referrerpolicy="no-referrer"
		></script>
	</head>
	<body>
		<button id="download-button">Download as PDF</button>
		<div id="invoice">
			<h1>Our Invoice</h1>
		</div>

		<script>
			const button = document.getElementById('download-button');

			function generatePDF() {
				// Choose the element that your content will be rendered to.
				const element = document.getElementById('invoice');
				// Choose the element and save the PDF for your user.
				html2pdf().from(element).save();
			}

			button.addEventListener('click', generatePDF);
		</script>
	</body>
</html>

In the example above, you only rendered the h1 title. However, you can render any HTML element, including images and tables. In the next example, you’ll use a more complex HTML structure.

To test this example locally, run a quick static server with:

npx serve -l 4111 .

Then open http://localhost:4111 in your browser to interact with the PDF download button.

html2pdf example

Advanced example: Downloading an invoice as a PDF

You can download an invoice template as a PDF by clicking this link. You can also generate the HTML for your own invoice on your backend if you prefer.

Similar to the example above, you’ll use the generatePDF() function to download the invoice as a PDF. However, this time, you’ll render the invoice template to the div element.

The end result will look like what’s shown below.

Preview of a fully rendered HTML invoice

2. pdfmake — Build PDFs from structured data

The pdfmake library generates PDF documents directly in the browser. It uses a layout and style configuration defined in a JSON-like structure within JavaScript, leveraging pdfkit under the hood for PDF generation.

Unlike html2pdf.js, which converts rendered HTML into a static image before converting it to a PDF, pdfmake explicitly defines the document structure in JavaScript, allowing for greater customization and text selection capabilities. This makes pdfmake ideal for situations where selectable text is necessary.

Installation

Add via CDN:

<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.72/pdfmake.min.js" integrity="sha384-qwfyMhpwB4dOhtxItT9bxl/sd7WQ95MvXSyLUgVz3oNRlvK/Ea7uT4O3rf1ZriyH" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.72/vfs_fonts.js" integrity="sha384-vD7JPR0c5DJMoqHXO7lJKsK9k6XPlL4LMoGK1jHsGBWoDzW9BGfaP7UWKIXHdz7L" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Or, install via npm:

npm install pdfmake

Generate PDFs from HTML using pdfmake

Define a generatePDF() function with your PDF content as a JavaScript object. Then use pdfmake to create and download the file. You can trigger it with a button in your HTML. While the example uses simple text and a table, pdfmake also supports lists, images, and advanced styling for more complex layouts.

<!DOCTYPE html>
<html>
	<head>
		<script
			src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.72/pdfmake.min.js"
			crossorigin="anonymous"
			referrerpolicy="no-referrer"
		></script>
		<script
			src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.72/vfs_fonts.js"
			crossorigin="anonymous"
			referrerpolicy="no-referrer"
		></script>
	</head>
	<body>
		<button onclick="generatePDF()">Download Invoice</button>

		<!-- Move script here so pdfMake is loaded -->
		<script>
			function generatePDF() {
				const docDefinition = {
					content: [
						{ text: 'Invoice', style: 'header' },
						{
							table: {
								body: [
									['Item', 'Qty', 'Price'],
									['Product 1', '2', '$10'],
									['Product 2', '1', '$20'],
									['Total', '', '$40'],
								],
							},
						},
					],
					styles: {
						header: { fontSize: 18, bold: true },
					},
				};

				pdfMake.createPdf(docDefinition).download('invoice.pdf');
			}
		</script>
	</body>
</html>

You can test the example by running:

npx serve -l 4111 .

pdfmake example

Benefits of using pdfmake

  • Selectable text — Unlike html2pdf.js, pdfmake preserves text as selectable and copyable.

  • Custom styling — Define font sizes, colors, and positioning with structured JavaScript.

  • Advanced layouts — Supports tables, lists, and multi-column designs.

3. Playwright — Server-side HTML-to-PDF rendering

Playwright is a framework that provides an API to control headless Chrome, Firefox, and WebKit browsers with a single API, allowing you to convert webpages into PDFs. This enables you to automate nearly all tasks that can be done manually in a browser, including generating PDFs from web content. The difference between Playwright and html2pdf is that you need to run Playwright on your server and serve the PDF to your users.

Installation

Create a new project and install [Playwright][npm-playwright]:

mkdir playwright-pdf-generation && cd playwright-pdf-generation
npm init --yes
npm install playwright

Before using Playwright, make sure the required browser binaries are installed by running:

npx playwright install

Basic PDF example

You’ll now create an index.js file where you’ll require Playwright, launch a new browser session, go to your invoice page, and save the PDF file:

// index.js

// Require Playwright.
const { chromium } = require('playwright');

(async function () {
	try {
		// Launch a new browser session.
		const browser = await chromium.launch();
		// Open a new page.
		const page = await browser.newPage();

		// Set the page content.
		await page.setContent('<h1>Our Invoice</h1>');

		// Generate a PDF and store it in a file named `invoice.pdf`.
		await page.pdf({ path: 'invoice.pdf', format: 'A4' });

		await browser.close();
	} catch (e) {
		console.error(e);
	}
})();

Running node index.js will generate invoice.pdf in your directory. But to let users download the PDF by clicking a button, set up a simple Node server using the http module.

The server listens on /generate-pdf, uses Playwright to render the page, and streams the PDF directly to the browser. When users click a download button linked to this endpoint, they’ll receive the PDF instantly — no file is saved to disk.

To do this, omit the path option in page.pdf() to get a buffer, set the response header to application/pdf, and send the buffer as the response. For all other routes, return a simple HTML page with a link to trigger the PDF download.

// index.js

const { chromium } = require('playwright');
const http = require('http');

// Create an instance of the HTTP server to handle the request.
http
	.createServer(async (req, res) => {
		if (req.url === '/generate-pdf') {
			// Making sure to handle a specific endpoint.
			const browser = await chromium.launch();
			const page = await browser.newPage();

			// Set the content directly or navigate to an existing page.
			await page.setContent(`
      <!DOCTYPE html>
      <html>
      <head>
        <title>Invoice</title>
      </head>
      <body>
        <h1>Our Invoice</h1>
        <p>Details about the invoice...</p>
      </body>
      </html>
    `);

			// By removing the `path` option, you'll receive a `Buffer` from `page.pdf`.
			const buffer = await page.pdf({ format: 'A4' });

			await browser.close();

			// Set the content type so the browser knows how to handle the response.
			res.writeHead(200, { 'Content-Type': 'application/pdf' });
			res.end(buffer);
		} else {
			// Respond with a simple instruction page.
			res.writeHead(200, { 'Content-Type': 'text/html' });
			res.end(
				'<h1>Welcome</h1><p>To generate a PDF, go to <a href="/generate-pdf">/generate-pdf</a>.</p>',
			);
		}
	})
	.listen(3000, () => {
		console.log('Server is running on http://localhost:3000');
	});

Open localhost:3000 in your browser, and you’ll see the instruction page. You can then navigate to /generate-pdf to open the PDF with the invoice.

playwright example

Ready to get started?

Thousands of companies, organizations, governments, and developers use Nutrient’s software to enable collaboration, signing, markup, and more in their apps.

Specific capabilities of each library

Each library brings its own strengths to HTML-to-PDF conversion:

  • html2pdf.js — Converts HTML/CSS into image-based PDFs in the browser. Best for simple, static layouts like receipts or certificates. May struggle with layout fidelity and resolution.

  • pdfmake — Builds PDFs from JavaScript objects. Ideal for structured documents like invoices or reports. Offers high customization, selectable text, and table support.

  • Playwright — Renders full HTML/CSS in a headless browser and outputs a pixel-perfect PDF. Great for complex layouts and server-side automation.

Performance overview

  • html2pdf.js — Lightweight and fast for small files, but browser memory limits can affect large documents.

  • pdfmake — Also client-side. Offers more flexibility but may slow down with complex content.

  • Playwright — Server-side rendering offloads work from the client. Suitable for large documents and consistent output at scale.

Common issues and troubleshooting

html2pdf.js

  • Blurry output — Use high-resolution images and scale properly.

  • CSS support — Limited styling; avoid modern layout features like flexbox or grid.

  • Large file sizes — Optimize HTML and images.

pdfmake

  • Layout complexity — Manual configuration required; break documents into smaller blocks when needed.

  • Font issues — Ensure custom fonts are embedded correctly.

Playwright

  • Requires a server — Setup is more involved than client-side tools.

  • Slower for large pages — Minimize dynamic scripts or unnecessary content before rendering.

Feature comparison

Feature html2pdf.js pdfmake Playwright
Ease of use Very easy Moderate Moderate
Installation Simple (CDN, npm) Simple (CDN, npm) npm
Runs in browser Check Check Cross
Text Selection Cross Check Check
CSS/HTML support Limited None Full
Customization Low High High
PDF quality Medium (image-based) High (structured content) Very high (browser rendering)
Performance Fast for small docs Good with structured layouts High for large/complex layouts
Use case Simple exports, receipts Invoices, reports, forms High-fidelity PDFs, automation
Pros No backend needed, easy setup Selectable text, layout control Pixel-perfect output, full CSS
Cons Blurry output, no selection Manual config, no HTML input Server setup, heavier runtime

Which one should you use?

  • For simple client-side exports, use html2pdf.js. It’s quick and works without a backend.

  • For structured, data-driven documents, pdfmake is ideal thanks to its flexibility and text support.

  • For high-fidelity, CSS-accurate rendering, go with Playwright, especially if you already run a Node.js backend.

Conclusion

For quick client-side PDF generation, html2pdf is a suitable choice. However, due to potential issues with file size and image quality, Playwright is recommended for server-side implementations to produce high-quality PDFs.

And if you want to save time with an out-of-the-box solution, check out PDF generation for Document Engine, which offers support for:

Document Engine combines PDF generation with powerful document operations that can streamline repetitive tasks such as merging and watermarking. Test it yourself with our free trial.

Additionally, Nutrient .NET SDK offers robust PDF-to-HTML conversion capabilities in C# .NET.

The method for this is GdPictureDocumentConverter.SaveAsHTML. This feature is now available in preview mode, allowing users to test it, build prototypes, and provide feedback. It was introduced in version 14.3.0. You can learn more in our detailed blog post.

Furthermore, we’re working on integrating PDF-to-HTML conversion functionality into Document Engine and DWS API, which will be available soon. Stay tuned for updates!

FAQ

What is the main advantage of using html2pdf for HTML-to-PDF conversion? html2pdf is user-friendly and enables you to generate PDFs directly on the client-side without requiring a server.
What are the drawbacks of using html2pdf for converting HTML to PDF? The primary drawback is that it generates PDFs as images, which can result in blurry text and larger file sizes, especially when using PNGs.
What makes pdfmake a strong option for HTML-to-PDF generation? pdfmake allows for highly customizable and high-quality PDFs with selectable and searchable text, making it suitable for professional documents.
Why should I consider using Playwright for HTML-to-PDF conversion? Playwright is ideal for server-side rendering, producing high-quality PDFs with selectable text and smaller file sizes. It’s particularly useful for complex PDFs and automated testing.
Which library should I use for client-side HTML-to-PDF generation? If you need a quick and easy client-side solution without a server setup, html2pdf is a simple and effective choice. However, for more complex documents requiring better text selection and styling, pdfmake might be a better alternative.
Is there a faster way to generate PDFs without writing all the logic myself? Yes. If you want a ready-to-use solution that handles more than just HTML-to-PDF conversion, check out PDF generation with Document Engine. It supports features like converting HTML forms into fillable PDFs, chaining document operations (e.g. adding cover pages or watermarks), and exposing everything via an HTTP API with flexible auth. It’s ideal if you want to skip low-level PDF logic and focus on building your app.
Authors
Hulya Masharipov
Hulya Masharipov Technical Writer

Hulya is a frontend web developer and technical writer at Nutrient who enjoys creating responsive, scalable, and maintainable web experiences. She’s passionate about open source, web accessibility, cybersecurity privacy, and blockchain.

Mahmoud Elsayad
Mahmoud Elsayad Web Engineer

Free trial Ready to get started?
Free trial