Mastering PDF Generation in the Browser: html2canvas + jsPDF Guide
This article explains how to convert HTML pages into multi‑page PDF files using html2canvas and jsPDF, covering basic setup, handling pagination, scaling, image insertion, and practical code examples to ensure readable, correctly paginated PDFs in front‑end projects.
When a business needs to export a web page as a readable PDF, a common solution is to convert the HTML to an image stream with html2canvas and then embed it into a PDF using jsPDF. This approach works well but encounters pagination‑cutoff issues when content exceeds one A4 page.
Using jsPDF and html2canvas to create a simple PDF
First, create a jsPDF instance and set page size and orientation. A minimal example:
var doc = new jsPDF({
orientation: 'landscape',
unit: 'in',
format: [4, 2]
});
doc.text('Hello world!', 1, 1);
doc.save('two-by-four.pdf');For richer pages, capture the DOM with html2canvas, which returns a base64 image that can be added to the PDF via addImage.
Introducing html2canvas
html2canvas(element, options)renders an HTML element (or selector) to a canvas. Common options include: allowTaint (default false) – whether to load cross‑origin images. backgroundColor (default #ffffff) – canvas background. useCORS (default false) – enable CORS for image loading. logging (default false) – output logs to console. width and height – canvas dimensions (defaults to element size). scale (default window.devicePixelRatio) – resolution scaling factor.
Demo code:
html2canvas(document.querySelector('#capture')).then(canvas => {
document.body.appendChild(canvas);
});Adding the canvas image to the PDF
Use
jsPDF.addImage(imageData, format, x, y, width, height, alias, compression). Parameters: imageData – base64 string, URL, or binary data. format – JPEG, PNG, or TIFF. x, y – coordinates in the PDF. width, height – image size. alias – optional name for later reference. compression – NONE, FAST, or SLOW.
pdf.addImage(imageData, 'JPEG', 0, 0, 10, 10);Multi‑page handling: scaling + offset looping
Two main problems appear in practice:
PDF content size does not match the original page.
Content taller than one page is cut off, producing a single‑page PDF.
The solution combines proportional scaling with a loop that adds pages and adjusts the vertical offset.
When the element height y exceeds the PDF height h, call addPage and render the next slice at -h offset. Repeat until the whole canvas is placed.
// First page
PDF.addImage(pageData, 'JPEG', 0, 0, x, y);
// Subsequent page
PDF.addImage(pageData, 'JPEG', 0, -h, x, y);Pagination cut‑off challenge
Both libraries require manual pagination. A practical method is to pre‑compute a pages array that stores the start Y‑position of each PDF page. While iterating over the canvas, maintain the current height and push a new page when the next element would cross the page boundary.
To avoid visual overlap, draw a white rectangle over the duplicated area using pdf.setFillColor(255,255,255) and pdf.rect(..., 'F').
pdf.setFillColor(255, 255, 255);
pdf.rect(x, y, Math.ceil(_width), Math.ceil(_height), 'F');Calculating element offsets (h1 and h2)
h1is the distance from the element’s top border to the printable area (after scaling). Obtain it with element.getBoundingClientRect().top. h2 is the element’s inner height, accessible via element.offsetHeight. Adjust the pages array by subtracting the first page’s offset so that values are relative to the actual printable element.
const newPages = pages.map(item => item - pages[0]);Online demo and source code
The article provides a live demo (https://pdf-demo-phi.vercel.app/) showing single‑page, multi‑page, custom header/footer, and landscape PDFs. The repository includes features such as direction control, corrected height calculations to avoid blank pages, custom header/footer support, and extensible pagination logic for complex components.
Key takeaways:
Use html2canvas + jsPDF for front‑end PDF generation.
Handle pagination by scaling, looping, and maintaining a pages offset array.
Cover cut‑off issues with white‑rect masking and accurate height calculations.
Customize orientation, header/footer, and pagination strategy as needed.
Goodme Frontend Team
Regularly sharing the team's insights and expertise in the frontend field
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
