Generating PDFs has become a crucial feature for many modern web applications, from creating invoices to producing complex data-driven reports. Over the years, various HTML to PDF tools have emerged, each with its own approach and capabilities. In this post, we’ll take a brief look at the evolution of PDF generation solutions — from wkhtmltopdf and PrinceXML (via DocRaptor) to Puppeteer and Playwright — and then walk through a short tutorial on using Playwright (Node.js) to generate a sample PDF in 2025.

A Brief History of PDF Generation Tools

wkhtmltopdf

wkhtmltopdf is a command-line tool that uses the WebKit rendering engine (the foundation of early Safari) to convert HTML and CSS into PDF. Historically, it was one of the most popular open-source solutions for server-side PDF generation.

➡️ How it works:

✅ Pros:

❌ Cons:

DocRaptor (PrinceXML)

DocRaptor is a cloud-based API that leverages the commercial PrinceXML rendering engine to convert HTML (and CSS) into high-quality PDF or Excel documents. PrinceXML itself is known for its advanced typesetting capabilities and thorough CSS support, including complex paged media features.

➡️ How it works:

✅ Pros:

❌ Cons:


Headless Chrome as a Game Changer

For a long time, generating PDFs that perfectly mirrored a modern browser’s rendering was difficult. Older engines like WebKit in wkhtmltopdf or specialized solutions (e.g., PrinceXML) often lagged behind the latest HTML/CSS/JS capabilities found in Chrome. That changed significantly when Google introduced Headless Chrome, allowing developers to run the browser without a visible UI.

Shortly after, Puppeteer and then Playwright emerged as powerful tools for automating Headless Chrome, enabling developers to generate PDFs and screenshots with precision.

Puppeteer

Puppeteer is a Node.js library that provides an extensive API for automating tasks in headless (or full) Chrome/Chromium. Initially released by the Google Chrome team, it quickly became popular for web scraping, testing, and HTML to PDF conversion.

➡️ How it works:

✅ Pros:

❌ Cons:

Playwright

Playwright was developed by Microsoft and shares many similarities with Puppeteer. It supports automated testing and browser manipulation for Chromium, Firefox, and WebKit, although PDF generation currently works only in Chromium. Despite that limitation, Playwright’s design and multiple language SDKs make it a powerful option for diverse teams.

➡️ How it works:

✅ Pros:

❌ Cons:

Puppeteer vs Playwright

Both Puppeteer and Playwright are powerful tools for PDF generation, leveraging the same Chromium engine. The choice between the two largely depends on your specific needs, such as your preferred programming language, the complexity of your project, and whether you need to automate multiple browsers beyond PDF generation.

Here’s a comparison to help you choose the right tool for your needs:


Step-by-Step Guide: Generating Invoice PDF with Playwright

In this quick example, we’ll show how to generate an invoice PDF by rendering an EJS template in Node.js and converting it to a PDF using Playwright.

The entire example is available on GitHub if you'd like to view or clone the full project.

1️⃣ Set Up the Project

1. Install Node.js.

Make sure you have Node.js installed. If not, download and install it from Node.js Website.

2. Create a New Project Directory.

Open a Terminal and run:

mkdir invoice-generator
cd invoice-generator

3. Initialize a New Node.js Project.

Create a package.json file with the following command:

npm init -y

4. Install Required Packages.

Install ejs for templating and playwright for generating PDFs:

npm install ejs playwright

2️⃣ Organize Your Project Structure

Here’s a suggested structure for better organization:

invoice-generator/
├── data/                   // Directory for data files
│   └── invoice-data.json   // JSON file for invoice data
├── templates/              // Directory for HTML templates
│   └── invoice.ejs         // Template for the invoice
├── generate-invoice.js     // Main script to generate PDFs
└── package.json            // Project configuration file

3️⃣ Create an EJS Template

You can find my example template on GitHub.

4️⃣ Add Invoice Data

You can find the example invoice data on GitHub.

5️⃣ Create the PDF Generator Script

Below is the complete script:

const ejs = require('ejs');
const fs = require('fs');
const {chromium} = require('playwright');
const path = require('path');

// Load invoice data from the JSON file
const invoiceData = JSON.parse(fs.readFileSync(path.join(__dirname, 'data', 'invoice-data.json'), 'utf8'));

(async () => {
    try {
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); // Generate unique timestamp

        // Render the EJS template to HTML
        const templatePath = path.join(__dirname, 'templates', 'invoice.ejs');
        const html = await ejs.renderFile(templatePath, invoiceData);

        // Launch a headless browser using Playwright
        const browser = await chromium.launch();
        const page = await browser.newPage();

        // Load the rendered HTML into the browser
        await page.setContent(html, {waitUntil: 'load'});

        // Generate the PDF and save it with a timestamped filename
        const pdfPath = `invoice-${timestamp}.pdf`;
        await page.pdf({
            path: pdfPath,
            format: 'A4',
            printBackground: true
            // Additional parameters can be added here
        });

        await browser.close();
        console.log(`PDF successfully created at: ${pdfPath}`);
    } catch (error) {
        console.error('An error occurred while generating the invoice:', error);
    }
})();

You can also find the complete script on GitHub.

️6️⃣ Run the Script

1. Open a terminal and navigate to the project directory:

cd invoice-generator

2. Run the script:

node generate-invoice.js

7️⃣ Check the Output

Once the script has executed successfully, you’ll find the following file in your project directory:

It’s done! You’ve successfully created the invoice PDF. 🎉

Below is a preview of the generated invoice PDF:

Conclusion

Playwright (and headless browsers in general) represent the modern standard for HTML to PDF conversion, providing excellent support for contemporary web layouts and interactive elements. While some projects still rely on wkhtmltopdf or specialized engines like PrinceXML, using headless Chromium ensures your PDFs accurately reflect the latest HTML/CSS/JS capabilities.

Don’t want to manage browser instances yourself? If you’re looking for a simpler route, you could opt for an API-based solution such as PDFBolt — an approach that offloads the maintenance and scaling concerns, letting you focus on your core application logic.

No matter which method you choose, we hope this brief history and tutorial will help you generate PDFs with confidence in 2025 and beyond!

Good Luck and Happy PDFing! 🚀