A few weeks ago, I ran into a challenge at work—I needed to generate PDFs from dynamically generated HTML content. Whether it was for invoices, reports, or downloadable documents, the requirement was clear: convert HTML to a well-formatted PDF using JavaScript.
At first, I assumed it would be straightforward, but I quickly realized there were multiple ways to achieve this, each with its own strengths and trade-offs. After testing different libraries and methods, I found the best solutions using jsPDF, html2pdf, and Puppeteer.
In this article, we’ll dive deep into these tools, explore how they work, and walk through step-by-step examples so you can seamlessly integrate HTML-to-PDF conversion into your own projects.
But that’s just the beginning! In upcoming articles, we’ll take a closer look at each library, breaking down their advanced features, customization options, and real-world applications. Whether you need client-side or server-side PDF generation, we’ll help you choose the right approach for your specific needs.
Let’s get started! 🚀
More practice
- How to Validate React.js Forms Without External Libraries
- Setup and Use NextAuth.js in Next.js 14 App Directory
- Implement Authentication with NextAuth in Next.js 14
- Set up Google and GitHub OAuth with NextAuth in Next.js 14
- Implement Authentication with Supabase in Next.js 14
- Setup Google and GitHub OAuth with Supabase in Next.js 14
- Implement Authentication with tRPC in Next.js 14
- Implement Authentication with tRPC API in Next.js 14
- Using tRPC with Next.js 14, React Query and Prisma
- How to Set Up and Use React Query in Next.js 14
- Using React Query with Supabase in Next.js App Router
- How to Setup React Query in Next.js 13 App Directory
- React Query and Axios: User Registration and Email Verification

Why Convert HTML to PDF?
Converting HTML to PDF is essential for various use cases:
- Preserving Content Formatting: PDFs maintain the layout and design of your HTML content.
- Sharing and Printing: PDFs are universally accessible and easy to share or print.
- Offline Access: Users can save and view PDFs without an internet connection.
- Compliance and Documentation: Many industries require documents in PDF format for legal or regulatory purposes.
- Improve Data Security: PDFs can be password-protected and restricted from editing, adding an extra layer of security to sensitive documents.
Understanding HTML to PDF Conversion
Before diving into the code, it’s important to understand how HTML to PDF conversion works in JavaScript. There are three main approaches:
- Client-side conversion: This happens entirely in the browser using JavaScript libraries. It’s suitable for simple documents and doesn’t require a server.
- Server-side conversion: This uses Node.js or another server-side technology to generate PDFs. It’s better for complex documents or high-volume processing.
- Hybrid approach: This combines client-side and server-side techniques for optimal results.
Popular JavaScript Libraries for HTML to PDF Conversion
Here are some widely used JavaScript libraries for converting HTML to PDF:
- jsPDF: A lightweight library for generating PDFs.
- html2pdf.js: A wrapper around jsPDF and html2canvas for easy HTML to PDF conversion.
- Puppeteer: A Node.js library for controlling headless Chrome, ideal for server-side PDF generation.
Step-by-Step Guide to Convert HTML to PDF
Turning HTML into a PDF doesn’t have to be complicated! In this section, we’ll walk through the process step by step, using different tools to make it quick and easy.
Step 1: Set Up Your Project
Let’s start by setting up a Vite project with vanilla JavaScript. Vite provides a fast development environment and optimized build process.
To set up this project from scratch, follow these steps:
- Create a new directory for your project
- Open a terminal and navigate to your project directory
- Initialize a new Vite project:
- pnpm
npm create vite@latest html-to-pdf-converter -- --template vanilla
- npm
npm create vite@latest html-to-pdf-converter -- --template vanilla
- yarn
yarn create vite html-to-pdf-converter --template vanilla
- bun
bun create vite@latest html-to-pdf-converter --template vanilla
- pnpm
- Change into the project directory:
cd html-to-pdf-converter
- Install the required dependencies:
pnpm install
If you used a different package manager, replacepnpm
withnpm
,yarn
, orbun
as needed.
Step 2: Install jsPDF Library
Install the jspdf
library using your preferred package manager:
# npm
npm install jspdf
# pnpm
pnpm add jspdf
# yarn
yarn add jspdf
# bun
bun add jspdf
Step 3: Create HTML Content
Modify the existing index.html
file in your Vite project to include the content you want to convert to PDF.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTML to PDF</title>
</head>
<body>
<h1>Hello, World!</h1>
<p>This is a sample HTML content to be converted into a PDF.</p>
</body>
</html>
Step 4: Convert HTML to PDF Using jsPDF
Open src/main.js
and add the following code to convert the HTML content to PDF.
import { jsPDF } from "jspdf";
// Create a new jsPDF instance
const doc = new jsPDF();
// Add HTML content to the PDF
doc.html(document.body, {
callback: function (doc) {
// Save the PDF
doc.save("output.pdf");
},
x: 10,
y: 10,
});
Step 5: Customize the PDF Output
You can customize the PDF by adding margins, fonts, and more:
doc.setFontSize(16);
doc.text("Customized PDF", 10, 20);
doc.setFontSize(12);
doc.text("This PDF has been customized using jsPDF.", 10, 30);
Step 5: Using jsPDF with html2canvas
For more control over the PDF generation process, you can use jsPDF and html2canvas separately. This approach gives you more flexibility but requires more code.
How It Works
- Use html2canvas to render the HTML element to a canvas
- Create a new jsPDF instance
- Convert the canvas to an image
- Add the image to the PDF
- Save or display the PDF
Implementation
Here’s the core code for converting HTML to PDF using jsPDF with html2canvas:
import { jsPDF } from 'jspdf';
import html2canvas from 'html2canvas';
async function generatePDF() {
// Get the HTML element
const element = document.getElementById('content-to-convert');
try {
// Render the element to a canvas
const canvas = await html2canvas(element, {
scale: 2,
logging: false,
useCORS: true
});
// Calculate dimensions
const imgWidth = 210; // A4 width in mm
const pageHeight = 297; // A4 height in mm
const imgHeight = canvas.height * imgWidth / canvas.width;
// Create PDF
const pdf = new jsPDF('p', 'mm', 'a4');
// Add image to PDF
const imgData = canvas.toDataURL('image/jpeg', 1.0);
pdf.addImage(imgData, 'JPEG', 0, 0, imgWidth, imgHeight);
// Save the PDF
pdf.save('document.pdf');
} catch (error) {
console.error('Error generating PDF:', error);
}
}
Alternative Libraries for HTML to PDF Conversion
While jsPDF is a great tool for generating PDFs, it has its limitations, especially when dealing with complex layouts and styles. Fortunately, there are other powerful libraries like html2pdf and Puppeteer that offer more flexibility and advanced features.
In this section, we’ll explore these alternatives, how they work, and when to use them for the best results.
Using html2pdf.js
The html2pdf.js library provides a straightforward way to convert HTML to PDF. It combines html2canvas for rendering and jsPDF for PDF generation.
# npm
npm install html2pdf.js
# pnpm
pnpm add html2pdf.js
# yarn
yarn add html2pdf.js
# bun
bun add html2pdf.js
How It Works
- The library captures the HTML element using html2canvas
- It converts the canvas to an image
- It adds the image to a PDF document using jsPDF
- It saves or displays the PDF
Implementation
Here’s the core code for converting HTML to PDF using html2pdf.js:
Example usage:
import html2pdf from 'html2pdf.js';
function generatePDF() {
// Get the HTML element
const element = document.getElementById('content-to-convert');
// Configure options
const options = {
margin: 10,
filename: 'document.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2 },
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
};
// Generate PDF
html2pdf().from(element).set(options).save();
}
Key Options Explained
- margin: Sets the margin around the content in the PDF (in mm)
- filename: The name of the downloaded PDF file
- image.type: The image format used (jpeg or png)
- image.quality: The quality of the image (0-1)
- html2canvas.scale: The scaling factor for rendering (higher values improve quality but increase file size)
- jsPDF.unit: The measurement unit (mm, cm, in)
- jsPDF.format: The paper size (a4, letter, etc.)
- jsPDF.orientation: The page orientation (portrait or landscape)
Using Puppeteer
Puppeteer is a Node.js library that provides a high-level API to control Chrome or Chromium over the DevTools Protocol. It’s an excellent choice for server-side HTML to PDF conversion because it uses the actual Chrome rendering engine, resulting in high-quality PDFs that accurately match what you see in the browser.
# npm
npm install puppeteer
# pnpm
pnpm add puppeteer
# yarn
yarn add puppeteer
# bun
bun add puppeteer
How It Works
- The client sends HTML content to a server endpoint
- The server uses Puppeteer to launch a headless Chrome instance
- Puppeteer loads the HTML content in a virtual browser page
- Puppeteer generates a PDF from the page
- The server sends the PDF back to the client
Server Implementation
First, you need to set up a Node.js server with Express and Puppeteer:
# npm
npm install express puppeteer
# pnpm
pnpm add express puppeteer
# yarn
yarn add express puppeteer
# bun
bun add express puppeteer
Then, create a server file (e.g., server.js
):
import express from 'express';
import puppeteer from 'puppeteer';
const app = express();
// Parse JSON request bodies (built-in middleware)
app.use(express.json({ limit: '50mb' }));
// API endpoint to generate PDF
app.post('/api/generate-pdf', async (req, res) => {
try {
const { html } = req.body;
if (!html) {
return res.status(400).json({ error: 'HTML content is required' });
}
const browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
await page.setContent(html, { waitUntil: 'networkidle0' });
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
margin: { top: '20mm', right: '20mm', bottom: '20mm', left: '20mm' }
});
await browser.close();
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'attachment; filename=document-puppeteer.pdf');
res.send(pdf);
} catch (error) {
console.error('Error generating PDF:', error);
res.status(500).json({ error: 'Failed to generate PDF' });
}
});
// Start the server
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Client Implementation
On the client side, you need to send the HTML content to the server:
async function generatePDFWithPuppeteer() {
try {
// Get the HTML content
const html = document.getElementById('html-content').value;
// Send the HTML to the server
const response = await fetch('/api/generate-pdf', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ html }),
});
if (!response.ok) {
throw new Error('Failed to generate PDF');
}
// Get the PDF as a blob
const blob = await response.blob();
// Create a URL for the blob
const url = URL.createObjectURL(blob);
// Create a link to download the PDF
const a = document.createElement('a');
a.href = url;
a.download = 'document-puppeteer.pdf';
document.body.appendChild(a);
a.click();
// Clean up
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (error) {
console.error('Error generating PDF with Puppeteer:', error);
alert('Error generating PDF. Check console for details.');
}
}
Advanced Customization Options
Sometimes, just converting HTML to PDF isn’t enough—you might need more control over the final output. Whether it’s customizing page layouts, adding headers and footers, tweaking margins, or embedding fonts, advanced customization options can make a huge difference.
To keep things concise, this section will focus specifically on jsPDF’s customization features, helping you fine-tune your PDFs with precision.
Custom Fonts
You can add custom fonts to your PDF using jsPDF:
import { jsPDF } from 'jspdf';
function addCustomFont() {
const pdf = new jsPDF();
// Add a custom font
pdf.addFont('fonts/OpenSans-Regular.ttf', 'OpenSans', 'normal');
// Use the custom font
pdf.setFont('OpenSans');
pdf.setFontSize(16);
pdf.text('This text uses a custom font', 20, 20);
pdf.save('custom-font-document.pdf');
}
Page Headers and Footers
Adding headers and footers to each page:
function addHeadersAndFooters() {
const pdf = new jsPDF();
const pageCount = 5; // Example: 5 pages
// Add content to each page
for (let i = 1; i <= pageCount; i++) {
if (i > 1) pdf.addPage();
// Add header
pdf.setFontSize(10);
pdf.setTextColor(100, 100, 100);
pdf.text('Company Name - Confidential', 105, 10, { align: 'center' });
// Add page content
pdf.setFontSize(12);
pdf.setTextColor(0, 0, 0);
pdf.text(`Page ${i} Content`, 20, 30);
// Add footer
pdf.setFontSize(10);
pdf.setTextColor(100, 100, 100);
pdf.text(`Page ${i} of ${pageCount}`, 105, 287, { align: 'center' });
}
pdf.save('document-with-headers-footers.pdf');
}
Tables
For complex tables, you can use jsPDF’s autoTable plugin:
import { jsPDF } from 'jspdf';
import 'jspdf-autotable';
function generateTablePDF() {
const pdf = new jsPDF();
// Define table data
const tableData = [
['Name', 'Email', 'Country', 'Age'],
['John Doe', 'john@example.com', 'USA', '38'],
['Jane Smith', 'jane@example.com', 'Canada', '45'],
['Bob Johnson', 'bob@example.com', 'UK', '29'],
['Alice Brown', 'alice@example.com', 'Australia', '32']
];
// Generate table
pdf.autoTable({
head: [tableData[0]],
body: tableData.slice(1),
startY: 20,
styles: {
fontSize: 12,
cellPadding: 5,
overflow: 'linebreak',
halign: 'center'
},
headStyles: {
fillColor: [41, 128, 185],
textColor: 255
},
alternateRowStyles: {
fillColor: [240, 240, 240]
}
});
pdf.save('table-document.pdf');
}
CSS Support
When using html2canvas, you can include CSS styles in your HTML:
function generateStyledPDF() {
// Create a container with styled content
const element = document.createElement('div');
element.innerHTML = `
<style>
.pdf-container {
font-family: Arial, sans-serif;
padding: 20px;
}
.pdf-header {
color: #2563eb;
border-bottom: 2px solid #2563eb;
padding-bottom: 10px;
}
.pdf-content {
margin-top: 20px;
}
.pdf-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.pdf-table th, .pdf-table td {
border: 1px solid #ddd;
padding: 8px;
}
.pdf-table th {
background-color: #f1f5f9;
}
</style>
<div class="pdf-container">
<h1 class="pdf-header">Styled Document</h1>
<div class="pdf-content">
<p>This document includes CSS styling that will be captured in the PDF.</p>
<table class="pdf-table">
<thead>
<tr>
<th>Item</th>
<th>Description</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Item 1</td>
<td>Description of item 1</td>
<td>$100.00</td>
</tr>
<tr>
<td>Item 2</td>
<td>Description of item 2</td>
<td>$150.00</td>
</tr>
</tbody>
</table>
</div>
</div>
`;
document.body.appendChild(element);
// Generate PDF with styles
html2pdf().from(element).save().then(() => {
document.body.removeChild(element);
});
}
Comparing the Three Methods
Let’s now compare the three popular tools for converting HTML to PDF: jsPDF, html2pdf, and Puppeteer. This will help you choose the best option for your project.
Feature | html2pdf.js | jsPDF + html2canvas | Puppeteer |
---|---|---|---|
Environment | Client-side | Client-side | Server-side |
Setup Complexity | Low | Medium | High |
Rendering Quality | Good | Good | Excellent |
CSS Support | Limited | Limited | Full |
JavaScript Support | Limited | Limited | Full |
Performance | Depends on browser | Depends on browser | Consistent |
File Size | Can be large | Can be large | Optimized |
Best For | Simple documents | Medium complexity | Complex layouts |
Conclusion
In this guide, we explored three powerful tools for converting HTML to PDF: jsPDF, html2pdf, and Puppeteer. Each method comes with its own strengths—jsPDF is great for client-side PDF generation, html2pdf offers an easy way to convert entire HTML elements, and Puppeteer provides a robust server-side solution for handling complex layouts.
Choosing the right tool depends on your specific needs, whether it’s generating lightweight PDFs in the browser or creating high-quality documents on the server. As you integrate these solutions into your projects, don’t hesitate to experiment with their customization options to achieve the perfect output.
In upcoming articles, we’ll take a deeper dive into each library, exploring their advanced features and capabilities to help you make the most of them.