Blog Post

How to Generate a PDF with JavaScript

Illustration: How to Generate a PDF with JavaScript
Information

This article was first published in March 2019 and was updated in November 2022.

A common use case in JavaScript for HTML-to-PDF conversion is giving your website visitors the ability to download HTML content as a PDF file. For example, invoices, concert tickets, and flight tickets tend to be available as PDF downloads.

In this post, we’ll take a look at two popular JavaScript libraries for converting HTML pages to PDFs. First, we’ll look at html2pdf. Then, we’ll go over Puppeteer. Lastly, we’ll cover the advantages and disadvantages of both libraries.

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

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/html2pdf.js/0.10.1/html2pdf.bundle.min.js" integrity="sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzZ8zBps+dvLusV+eNQATqgA/HdeKFVgA5v3S/cIrLF7QnIg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

NPM

Or, download the package from npm:

npm install html2pdf.js

Manual Download

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 begin, define a generatePDF() function that will get the element you want to download as a PDF. Then, call html2pdf with that element to download it directly on your users’ client. Next, call this 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, tables, and more. In the next example, you’ll use a more complex HTML structure.

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

Information

Interact with the sandbox by clicking the left rectangle icon and selecting Editor > Show Default Layout. To edit, sign in with GitHub — click the rectangle icon again and choose Sign in. To preview the result, click the rectangle icon once more and choose Editor > Embed Preview. For the full example, click the Open Editor button. Enjoy experimenting with the project!

Puppeteer

Puppeteer is a Node library that gives you an API to control a headless Chrome or Chromium instance. This allows you to do most things that you’re also able to do manually in the browser, and one of those things is generating a PDF from your website. The difference between Puppeteer and html2pdf is that you need to run Puppeteer on your server and serve the PDF to your users.

Installing Puppeteer

For the Puppeteer 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:

npm init --yes
  1. After initializing the Node project, you’ll have a package.json in your directory. Now it’s time to add Puppeteer as a dependency to your project:

npm install puppeteer

If you’re using an M1 Mac, first install Homebrew and install Chromium via homebrew:

brew install chromium

Then, try to run the npm install command again.

  1. Your package.json will look similar to this:

{
	"name": "puppeteer-pdf-example",
	"version": "1.0.0",
	"description": "Example of how to generate a PDF with Puppeteer",
	"main": "index.js",
	"license": "MIT",
	"private": false,
	"dependencies": {
		"puppeteer": "19.0.0"
	}
}

Using Puppeteer

This example assumes you have your page with the invoice running on localhost:8000.

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

// index.js

// Require Puppeteer.
const puppeteer = require('puppeteer');

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

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

		// Store the PDF in a file named `invoice.pdf`.
		await page.pdf({ path: 'invoice.pdf', format: 'A4' });

		await browser.close();
	} catch (e) {
		console.log(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 require http in your script. Start a small server and set the headers to application/pdf to tell the browser that you’ll respond with a PDF. Instead of writing to a file when creating the PDF, you’ll directly serve the buffer that’s returned from page.pdf. To make this possible, you just have to remove the path option:

// index.js
const puppeteer = require('puppeteer');
const http = require('http');

// Create an instance of the HTTP server to handle the request.
http
	.createServer(async (req, res) => {
		// Set the content type so the browser knows how to handle the response.
		res.writeHead(200, { 'Content-Type': 'application/pdf' });

		const browser = await puppeteer.launch();
		const page = await browser.newPage();
		await page.goto('http://localhost:3000');
		// By removing the `path` option, you'll receive a `Buffer` from `page.pdf`.
		const buffer = await page.pdf({ format: 'A4' });

		await browser.close();

		// You can serve this buffer to the browser directly.
		res.end(buffer);
	})
	.listen(3000);

However, sometimes you won’t want to serve a page from your web server and you’ll instead want to use the HTML you generated on your server directly. This can easily be done with Puppeteer’s setContent function, which takes the HTML that needs to get rendered on the site as an argument:

// index.js
const puppeteer = require('puppeteer');
const http = require('http');

http
	.createServer(async (req, res) => {
		const browser = await puppeteer.launch();
		const page = await browser.newPage();
		await page.setContent(`
<!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">
</head>
<body>
  <div id="invoice">
    <h1>Our Invoice</h1>
  </div>
</body>
</html>
`);
		const buffer = await page.pdf({ format: 'A4' });
		await browser.close();

		res.end(buffer);
	})
	.listen(3000);

Open localhost:3000 in your browser and you’ll see a PDF with the invoice.

Advantages and Disadvantages

The biggest advantage of html2pdf is that it’s really easy to generate a PDF from your HTML on your client, which means you don’t need a server at all. However, the disadvantage of this approach is that html2pdf just takes a screenshot of your site and generates a PDF out of it, which means the text will look blurry when you zoom in or if you’re using a Retina display.

You can configure html2pdf to use PNGs instead of JPEGs, but this causes the size of the PDF to increase dramatically: For the same resolution where the JPEG version is just 280 KB, the PNG version is 28 MB.

To counter this, consider choosing a larger resolution to make your PDF look sharper. To do this, change the generatePDF function and add the parameters for the scale to it:

function generatePDF() {
	// Choose the element that your invoice is rendered in.
	const element = document.getElementById('invoice');
	// Choose the element and save the PDF for your user.
	html2pdf()
		.set({ html2canvas: { scale: 4 } })
		.from(element)
		.save();
}

The biggest advantage of Puppeteer is that it creates an actual PDF file with text content instead of just using an image. You’ll be able to select and copy the text from the PDF, and you don’t need to worry about resolution since it’ll always be sharp. Additionally, the file size is significantly lower; compared to the html2pdf example, Puppeteer’s resulting PDF is about four times smaller.

The main disadvantage of using Puppeteer is that you’ll need to run a server instead of generating the PDF on the client.

Conclusion

If you need something quickly and don’t want to build anything on your server to create PDF files, you’re good to go with html2pdf. However, considering the increased file size and resulting image, we recommend building a component on your server with Puppeteer so that you can serve nice PDFs.

And if you want to save time with an out-of-the-box solution, make sure to check out PDF Generation for PSPDFKit Processor, which offers support for:

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

Share Post
Free 60-Day Trial Try PSPDFKit in your app today.
Free Trial

Related Articles

Explore more
PRODUCTS  |  Web • Releases • Components

PSPDFKit for Web 2024.3 Features New Stamps and Signing UI, Export to Office Formats, and More

PRODUCTS  |  Web • Releases • Components

PSPDFKit for Web 2024.2 Features New Unified UI Icons, Shadow DOM, and Tab Ordering

PRODUCTS  |  Web

Now Available for Public Preview: New Document Authoring Experience Provides Glimpse into the Future of Editing