Blog Post

Generate PDF Invoices with PDFKit on Node.js

Illustration: Generate PDF Invoices with PDFKit on Node.js

A lot of Node.js web applications require dynamic PDF generation. When researching possible solutions, developers often stumble upon PDFKit, a JavaScript PDF generation library for Node.js and the browser.

In this tutorial, we’ll explore how you can use PDFKit to generate PDF invoices with both static and dynamic data in your Node.js app. If you want to skip directly to the solution, you can take a look at the complete example on GitHub.

Getting Started

Before we begin, we start a new npm package and pull in the dependencies we’ll need for the project:

mkdir pdfkit-invoice-node
cd pdfkit-invoice-node
npm init -y
npm install --save pdfkit

With that set up, we’re ready to start creating our first invoice.

Data Model

To make our invoice generator reusable, we design a simple data model in the form of a JavaScript object. This model will hold all the information we need to generate the invoice. Here’s an example of how such an invoice object might look:

const invoice = {
	shipping: {
		name: 'John Doe',
		address: '1234 Main Street',
		city: 'San Francisco',
		state: 'CA',
		country: 'US',
		postal_code: 94111,
	items: [
			item: 'TC 100',
			description: 'Toner Cartridge',
			quantity: 2,
			amount: 6000,
			item: 'USB_EXT',
			description: 'USB Cable Extender',
			quantity: 1,
			amount: 2000,
	subtotal: 8000,
	paid: 0,
	invoice_nr: 1234,

In the above object, we have a shipping key that contains all shipping information that should be printed on the invoice. The items key contains an array of all items we want to be printed on the invoice (the amount is the sum for all pieces of this item in cents). The subtotal key contains the sum of all items in cents, and the paid field allows you to specify how much was already paid for this invoice. invoice_nr is used to identify the invoice.

Generating the PDF Invoice

With this data model, we’re now ready to generate a PDF file. We start by creating a function, createInvoice(invoice, path), which uses the invoice object to create a valid PDF invoice and then saves it to a file located at path.

Our invoice will consist of four visual parts:

  1. A header that contains information about our company, including our company’s logo.

  2. The address of the customer.

  3. A table of all ordered items.

  4. A generic footer to wrap up the invoice.

To create an empty PDF document, we use the PDFDocument constructor of pdfkit. We also create stub methods for the four sections above:

const fs = require('fs');
const PDFDocument = require('pdfkit');

function createInvoice(invoice, path) {
	let doc = new PDFDocument({ margin: 50 });

	generateCustomerInformation(doc, invoice);
	generateInvoiceTable(doc, invoice);


The code above will create an empty PDF document and store it at path.

Adding Static Data to the PDF

Now that we have an empty PDF page, it’s time to fill it with more information. Let’s start with the two static sections, Header and InvoiceTable.

The API of pdfjs requires us to chain all drawing commands on the PDFDocument instance. Images can be loaded using the .image() method:

function generateHeader(doc) {
	doc.image('logo.png', 50, 45, { width: 50 })
		.text('ACME Inc.', 110, 57)
		.text('123 Main Street', 200, 65, { align: 'right' })
		.text('New York, NY, 10025', 200, 80, { align: 'right' })

function generateFooter(doc) {
		'Payment is due within 15 days. Thank you for your business.',
		{ align: 'center', width: 500 },

The .image() and .text() methods take x and y as the second and third argument, respectively. We use those coordinates to lay out the text however we want.

Screenshot of a PDF invoice in the PSPDFKit for Web reader. The PDF contains only a header and a footer.

If we run our Node.js script, it’ll generate the above PDF. But now we need to add dynamic data to it!

Adding Dynamic Data to the PDF

To add the dynamics bits to our PDF, we rely on the data model outlined earlier. Let’s start with the customer information section. Here, we want to print the address of our customer. We can do this by concatenating the dynamic values to the PDF rendering commands:

function generateCustomerInformation(doc, invoice) {
	const shipping = invoice.shipping;

	doc.text(`Invoice Number: ${invoice.invoice_nr}`, 50, 200)
		.text(`Invoice Date: ${new Date()}`, 50, 215)
		.text(`Balance Due: ${invoice.subtotal - invoice.paid}`, 50, 130)

		.text(, 300, 200)
		.text(shipping.address, 300, 215)
			`${}, ${shipping.state}, ${}`,

The above code block uses a simplified layout. For a complete example, check out the example code on GitHub.

For the table, we’ll be using a helper function that draws one row of the column. We can later loop over all the items in our invoice model and create table rows for them. To make sure the helper renders everything in one row, we set the y coordinate as an argument to the function. Every row will have five columns, c1 to c5:

function generateTableRow(doc, y, c1, c2, c3, c4, c5) {
		.text(c1, 50, y)
		.text(c2, 150, y)
		.text(c3, 280, y, { width: 90, align: 'right' })
		.text(c4, 370, y, { width: 90, align: 'right' })
		.text(c5, 0, y, { align: 'right' });

We can use this helper function to render one row for every line item:

function generateInvoiceTable(doc, invoice) {
	let i,
		invoiceTableTop = 330;

	for (i = 0; i < invoice.items.length; i++) {
		const item = invoice.items[i];
		const position = invoiceTableTop + (i + 1) * 30;
			item.amount / item.quantity,

To keep the code examples in this post concise, I’ve stripped out table headers and footers, along with some utility functions for formatting currency and dates. You can take a look at the complete example code on GitHub.

In any case, once we add the table headers and footers, we’ll have a full invoice generated on demand using pdfkit on Node.js.

Screenshot of a PDF invoice in the PSPDFKit for Web reader.


PDFKit allows us to generate PDF documents in Node.js and the browser. It’s ideally suited for tasks like dynamically generating PDF invoices for your web server. In the example above, we learned about PDFKit and used it to dynamically generate a PDF invoice from a simple object-based data model. You can check out the source code on GitHub.

If you want to display the PDF in a powerful PDF viewer or add advanced functionality such as digital signatures or PDF annotations, we recommend you give our server-backed deployment of PSPDFKit for Web a try.

If you’re interested in automating the PDF generation process, take a look at our PDF Generation SDK or our PDF Generation API, both of which let you generate PDF invoices from an HTML document. We also have a blog post that guides you through generating PDFs from HTML with Node.js.

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

Related Articles

Explore more
TUTORIALS  |  Web • SharePoint • How To • PDF Viewer

SharePoint PDF Viewer: Integrating On-Premises with PSPDFKit

TUTORIALS  |  Web • SharePoint • How To • PDF Viewer

SharePoint PDF Viewer: Integrating Web Parts with PSPDFKit

PRODUCTS  |  Web • SharePoint • Teams • OneDrive • Releases

Introducing PSPDFKit for SharePoint, Microsoft Teams, and OneDrive