Blog post

Top JavaScript libraries for converting HTML to PDF

Illustration: Top JavaScript libraries for converting HTML to PDF

Converting HTML to PDF is a common requirement for web developers, whether it’s for invoices, tickets, or reports. The versatility and reliability of the Portable Document Format (PDF) make it an essential tool for sharing documents while maintaining formatting across various devices. This post will explore the top three JavaScript libraries for HTML-to-PDF conversion: html2pdf, pdfmake, and Playwright. It’ll discuss their features, pros, and cons, and help you decide which library is the best fit for your needs.

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.

1. Client-side PDF generation using html2pdf

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

To get started, install the html2pdf library.

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

Alternatively, download the package from npm:

npm install html2pdf.js

Downloading the library manually

You can also download the bundled html2pdf JavaScript library directly from the html2pdf.js GitHub repository.

After you’ve downloaded the library, add a script tag to your HTML file:

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

Using html2pdf to generate PDFs

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>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<title>HTML-to-PDF Example</title>
		<meta
			name="viewport"
			content="width=device-width, initial-scale=1"
		/>
		<!-- 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.

html2pdf example

Downloading an invoice as a PDF

We provided an invoice template to download as a PDF. You can download it 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. JavaScript PDF generation using pdfmake

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.

While html2pdf converts rendered HTML directly into a static image that’s later converted to a PDF file, retaining the layout and styles of the webpage, the text content becomes unselectable. Meanwhile, pdfmake requires explicitly defining the document’s structure and styles in JavaScript, offering more precise control over the PDF output. The biggest advantage of pdfmake is that the generated text content can be selected and copied, making it a better option when text selection is important.

Installation

To get started, install the pdfmake library.

Adding pdfmake via CDN

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/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>

Installing pdfmake via npm

Alternatively, download the package from npm:

npm install pdfmake

Generating PDFs with pdfmake

To begin, define a generatePDF() function that will be used to specify the document configuration using a structured JavaScript object. Then, use pdfmake to generate and download the PDF based on this configuration. Next, associate this function with a download button in index.html:

<!--index.html-->
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<title>HTML-to-PDF PDFMake Example</title>
		<meta
			name="viewport"
			content="width=device-width, initial-scale=1"
		/>
		<!-- pdfmake CDN link -->
		<script
			src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.72/pdfmake.min.js"
			crossorigin="anonymous"
			referrerpolicy="no-referrer"
		></script>
		<!--vfs_fonts.js is essential for embedding fonts into PDF documents created with pdfmake-->
		<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()">Generate Invoice PDF</button>

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

			function generatePDF() {
				const docDefinition = {
					content: [
						{
							columns: [
								{
									text: '&',
									style: 'logo',
								},
								{
									stack: [
										{ text: 'Company', style: 'companyName' },
										{
											text: '[COMPANY INFO]',
											style: 'companyInfo',
										},
									],
									alignment: 'right',
								},
							],
							margin: [0, 20, 0, 30],
						},
						{
							canvas: [
								{
									type: 'line',
									x1: 0,
									y1: 5,
									x2: 515,
									y2: 5,
									lineWidth: 2.5,
									lineColor: 'black',
								},
							],
							margin: [0, 10, 0, 20],
						},
						{
							columns: [
								{
									text: [
										{
											text: 'Billed To:\n',
											style: 'billedToLabel',
										},
										{
											text: 'Company Ltd.\n',
											style: 'billedTo',
										},
										{
											text:
												'Address\nCountry\nVAT ID: ATU12345678\n',
											style: 'billedTo',
										},
									],
									width: '60%',
								},
								{
									text: [
										{
											text: 'Invoice #\n',
											style: 'invoiceLabel',
										},
										{
											text: '123456\n',
											style: 'invoiceNumber',
										},
										{
											text: 'Payment Date:\n',
											style: 'invoiceLabel',
										},
										{
											text: 'November 22nd 2021\n',
											style: 'invoiceDate',
										},
										{
											text: 'Payment Method:\n',
											style: 'invoiceLabel',
										},
										{
											text: 'Bank Transfer',
											style: 'paymentMethod',
										},
									],
									alignment: 'right',
									width: '40%',
								},
							],
							margin: [0, 10, 0, 20],
						},
						{
							style: 'itemsTable',
							table: {
								widths: ['*', 75, 100, 100],
								headerRows: 1,
								body: [
									[
										{
											text: 'Description',
											style: 'tableHeader',
										},
										{ text: 'QTY', style: 'tableHeader' },
										{
											text: 'Unit Price',
											style: 'tableHeader',
										},
										{ text: 'Total', style: 'tableHeader' },
									],
									['Product 1', '2', '$10.00', '$20.00'],
									['Product 2', '5', '$15.00', '$75.00'],
									['Product 3', '1', '$25.00', '$25.00'],
									[
										{
											text: 'Total',
											colSpan: 3,
											alignment: 'right',
											style: 'totalLabel',
										},
										{},
										{},
										'$120.00',
									],
								],
							},
							layout: 'lightHorizontalLines',
						},
					],
					styles: {
						logo: { fontSize: 30, bold: true, color: '#4736e7' },
						companyName: {
							fontSize: 16,
							bold: true,
							color: '#333333',
						},
						companyInfo: { fontSize: 10, color: '#555555' },
						billedToLabel: {
							bold: true,
							fontSize: 12,
							color: '#333333',
						},
						billedTo: {
							fontSize: 10,
							color: '#555555',
							lineHeight: 1.5,
						},
						invoiceLabel: {
							fontSize: 10,
							bold: true,
							color: '#333333',
						},
						invoiceNumber: { fontSize: 10, color: '#555555' },
						invoiceDate: { fontSize: 10, color: '#555555' },
						paymentMethod: { fontSize: 10, color: '#555555' },
						tableHeader: {
							bold: true,
							fontSize: 10,
							color: 'black',
						},
						totalLabel: { bold: true, fontSize: 10 },
					},
					defaultStyle: {
						columnGap: 20,
					},
				};

				pdfMake
					.createPdf(docDefinition)
					.download('styled-invoice.pdf');
			}

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

In the example above, the document configuration includes simple text and a line element. However, you can define a wide range of elements in your PDFs with pdfmake, including tables, lists, images, and styled text. In the next example, you’ll see how to configure a more complex document layout using pdfmake’s powerful styling and layout options.

You can test the example by running:

npx serve -l 4111 .

pdfmake example

3. Convert HTML to PDF using Playwright

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 allows 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.

Installing Playwright

For the Playwright example, you’ll build a small Node.js server and serve your user a PDF that gets downloaded.

  1. Begin by creating a new Node project:

mkdir playwright-pdf-generation && cd playwright-pdf-generation
npm init --yes
  1. After initializing the Node project, you’ll have a package.json file in your directory. Now it’s time to add Playwright to your project as a dependency:

npm install playwright
  1. Your package.json file will look similar to this:

{
	"name": "playwright-pdf-generation",
	"version": "1.0.0",
	"description": "Example of how to generate a PDF with Playwright",
	"main": "index.js",
	"license": "MIT",
	"private": false,
	"dependencies": {
		"playwright": "^1.46.0"
	}
}

Generating PDFs with Playwright

Before you start, make sure you have the latest version of Playwright installed. If you don’t, you can update it by running:

npx playwright install

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 PDF and store in a file named `invoice.pdf`.
		await page.pdf({ path: 'invoice.pdf', format: 'A4' });

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

When you now run the script via node index.js, you’ll see a nicely generated PDF with the name invoice.pdf in your directory.

However, what you actually want is to serve your users a PDF when they click a download button. For this, you’ll use the http module from Node and respond with the invoice PDF when a user goes to your page on localhost:3000.

First, you need to integrate the http module in your script. The server will listen for requests on the /generate-pdf endpoint, and when accessed, it’ll initialize Playwright’s Chromium to open a new page and set the content. Instead of writing to a file when creating the PDF, you’ll serve the buffer that’s returned from page.pdf directly by omitting the path option in the page.pdf() method. The server will then set the HTTP headers to application/pdf and send the buffer directly to the client, ensuring the browser interprets it as a PDF file.

For non-PDF requests, provide a basic HTML page with instructions to navigate to the PDF generation endpoint:

// 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

Specific capabilities of each library

When choosing a PDF generation library, it’s essential to understand the specific capabilities each one offers. Below is a more detailed breakdown of how html2pdf, pdfmake, and Playwright handle different PDF requirements:

  • html2pdf — This library is best for simple layouts. It converts HTML and CSS into PDFs, but it struggles with complex layouts and extensive styling. It supports images but may have issues with scaling and resolution.

  • pdfmake — This library allows for more complex document structures, including tables, headers, and footers. It supports custom fonts, advanced styling, and multi-column layouts. It’s ideal for business reports and invoices.

  • Playwright — Unlike the other two libraries, Playwright automates the rendering of web pages into PDFs at a browser level. It fully supports CSS, including complex styles, grid layouts, and high-resolution images. This makes it an excellent choice for creating pixel-perfect PDFs.

Performance considerations

When generating PDFs, performance is a critical factor, especially when dealing with large documents or complex layouts. Here’s how the libraries compare:

  • html2pdf — As a client-side library, it runs entirely in the browser, making it fast for small documents. However, large PDFs can lead to memory issues and slow performance.

  • pdfmake — This is also a client-side solution, but it allows greater customization, which can lead to slower processing times for large documents.

  • Playwright — This runs server-side, meaning it offloads processing from the client. This is particularly useful for large documents or applications requiring consistent rendering across multiple users.

Common issues and troubleshooting

Each library comes with its challenges, so understanding common issues can help you avoid pitfalls during implementation.

html2pdf

  • Blurry text and images — Ensure images are high-resolution before conversion.

  • CSS limitations — Some styles may not be fully supported. Avoid using advanced CSS like flexbox and grid where possible.

  • Large file sizes — Optimize images and minimize unnecessary elements in your HTML.

pdfmake

  • Complex layout issues — pdfmake requires manual configuration for advanced layouts. Consider breaking content into smaller sections.

  • Font rendering problems — Ensure you embed custom fonts correctly and test across different devices.

Playwright

  • Requires server setup — Unlike the other libraries, Playwright needs a server environment, which can be complex to configure.

  • Slow rendering with large documents — Optimize by reducing unnecessary JavaScript execution on the page before conversion.

Comparative analysis of HTML-to-PDF conversion libraries

To help you choose the best library for your needs, here’s a comparison table summarizing the key features, pros, and cons of each library.

Feature/criteria html2pdf pdfmake Playwright
Ease of use Very easy Moderate Moderate
Installation Simple (CDN, npm) Simple (CDN, npm) Simple (npm)
Dependencies html2canvas, jsPDF PDFkit Browser automation library
PDF quality Moderate High High
Server-side rendering No No Yes
Customization Limited High High
Text selection No Yes Yes
Image handling Good Good Excellent
Performance Good (client-side) Good (client-side) High (server-side)
Use case examples Certificates, invoices Business reports, invoices Invoices, automated testing
Pros Easy to use, no server needed Highly customizable, no dependencies Server-side rendering, high quality
Cons Blurry PDFs, non-selectable text Requires manual customization Requires server-side setup

By comparing these features, you can better decide which library suits your specific project requirements:

  • Client-side applications — If you need a quick and easy solution without server-side setup, html2pdf is user-friendly and easy to implement quickly.

  • Customizable and high-quality PDFs — For greater customization and higher quality, pdfmake is a robust choice.

  • Server-side rendering and complex PDFs — For high performance and the ability to handle complex PDFs, Playwright is ideal, though it requires a server setup.

Best practices for JavaScript PDF generation

To ensure high-quality and efficient PDF generation, follow these best practices:

  1. Use a clear and concise template

  • Keep HTML simple and structured to avoid layout inconsistencies in the final PDF.

  • Minimize excessive styling and avoid relying on complex positioning.

  1. Optimize images and fonts

  • Use vector-based formats (SVG) where possible for crisp images.

  • Embed fonts to ensure consistent rendering across devices.

  1. Ensure layout consistency

  • Use a uniform approach to margins, font sizes, and alignment.

  • Test on different screen sizes to prevent unexpected breaks.

  1. Test and validate PDF output

  • Use tools like Adobe Acrobat to check rendering accuracy.

  • Validate for accessibility to ensure compatibility with assistive technologies.

  1. Handle page breaks correctly

  • Use CSS properties like page-break-before and page-break-after for better control.

  • Avoid breaking important elements (e.g. tables, images) across pages.

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? 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? 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 generating PDFs? 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 PDF generation? 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 applications? For quick and easy client-side PDF generation, html2pdf is a suitable choice.
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