Generating PDFs in a modern web app sounds simple—until you actually try to do it. Between browser limitations, inconsistent HTML rendering, sandboxed environments like Electron, and the need for pixel-perfect output, developers quickly realize there’s no one-size-fits-all solution. Different tools solve different problems. Some libraries are great for building PDFs from scratch, others are ideal for filling existing templates, and some are the only reliable way to recreate a webpage exactly as it appears on screen.
Some tools excel at building PDFs from scratch with custom layouts. Others are perfect for filling existing PDF templates. And a completely different set of tools is required when you need a PDF that looks exactly like the webpage your users see.
If you choose the wrong approach, you’ll waste hours fighting broken CSS, missing fonts, cut-off pages, or slow rendering.
This guide cuts through the noise and breaks down the most practical, battle-tested methods — from browser printing and React-based rendering to low-level PDF manipulation, HTML conversion engines, and Electron-specific techniques — so you can choose the right strategy for your app and avoid a weekend of debugging
The multiple ways to print PDFs
- Comparing methods
- Browser shortcut -
Ctrl + P(Windows) orCmd + P(Mac) - React-pdf
- pdf-lib
- jsPDF
- Browser shortcut -
- Recommendations
- Client side vs Server side print
Comparing methods
Browser "Print to PDF" (Fastest, but not the cleanest)
Every browser can do this.
How does it work:
- Open your webpage
- Press Ctrl + P (Windows) or Cmd + P (Mac)
- Select Save as PDF
- Adjust margins, background graphics, and layout
Pros:
- Simple
- No setup needed
Cons:
- Often messy layouts
- Doesn't handle long pages well
- Breaks when your website uses animations, dynamic content, or infinite scrolling
- Electron app doesn't work with
ctrl+p. Use Electron'swebContents.printToPDF()API.
// main.js
const { BrowserWindow, ipcMain, dialog } = require('electron');
ipcMain.handle('export-current-view-to-pdf', async (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
const pdfBuffer = await win.webContents.printToPDF({
printBackground: true,
marginsType: 1, // default
pageSize: 'A4',
});
const { filePath } = await dialog.showSaveDialog(win, {
title: 'Save PDF',
defaultPath: 'export.pdf',
filters: [{ name: 'PDF', extensions: ['pdf'] }],
});
if (!filePath) return { canceled: true };
const fs = require('fs');
fs.writeFileSync(filePath, pdfBuffer);
return { canceled: false, filePath };
});
Renderer:
// renderer (React)
await window.electron.invoke('export-current-view-to-pdf');
React-pdf
A react-based renderer that lets you build PDFs using React components, similar to building a UI. Think of it like "writing JSX for a PDF file".
When to use react-pdf?
- A custom-designed PDF layout (contracts, invoices, quotes, receipts)
- Full control over spacing, fonts, margins, page breaks
- A PDF that does NOT look like your website, instead a professionally formatted document
- Server-side or client-side generation with React/Next.js
Pros:
- Custom layouts
- Use your React knowledge (JSX components)
- Good for complex multi-page PDFs
- Supports images, tables, styling
Cons:
- Slow for heavy PDFs
- Not ideal for turning an existing webpage into a PDF
- Styling is limited compared to CSS
- Leaning curve (if no previous experience of React)
import { Page, Text, Document, StyleSheet } from '@react-pdf/renderer';
const styles = StyleSheet.create({
page: { padding: 30 },
title: { fontSize: 20, marginBottom: 10 }
});
export const MyPDF = () => (
<Document>
<Page style={styles.page}>
<Text style={styles.title}>INRI Paint & Wall LLC Estimate</Text>
<Text>Total: $6,800</Text>
<Text>Includes wood replacement, caulking, and painting.</Text>
</Page>
</Document>
);
pdf-lib
A low-level PDF manipulation library. It's extremely powerful, you can modify existing PDFs, merge, split, add pages, add images, write text anywhere. Think of it like "photoshop but PDFs in JavaScript".
You can take a PDF contract, then fill in:
- Client name
- Address
- Price
- Dates
- Signature image
Pros:
- Modify existing PDFs
- Compact and fast
- Very flexible
- Supports images, font embedding
- No React required
Cons:
- Not meant for designing full layouts from scratch
- Harder to position elements (you must use coordinates)
- Does Not covert webpages to PDFs
import { PDFDocument } from 'pdf-lib';
const existingPdf = await fetch('/contract.pdf').then(res => res.arrayBuffer());
const pdfDoc = await PDFDocument.load(existingPdf);
const pages = pdfDoc.getPages();
const firstPage = pages[0];
firstPage.drawText('Client: Steve', { x: 50, y: 700 });
firstPage.drawText('Price: $6,800', { x: 50, y: 680 });
const pdfBytes = await pdfDoc.save();
// download or return via API
jsPDF
A very popular, older library for generating PDFs in the browser. Works with HTML or manual drawing commands. Think of it like "Quick PDF generator, simple and lightweight".
You can take sections of your webpage and convert it. You can also manually write text, draw lines, and add images.
Pros:
- Easy to use
- Supports HTML-to-PDF
- Good for simple PDFs
- Fast on small documents
Cons:
- Not good for complex layouts
- HTML rendering is inconsistent
- Not great for long PDFs
- Struggles with modern CSS
import jsPDF from 'jspdf';
const pdf = new jsPDF();
pdf.text("INRI Paint & Wall LLC", 10, 10);
pdf.text("Exterior Paint Quote: $6,800", 10, 20);
pdf.save("quote.pdf");
Headless Browser Rendering (Puppeteer/Playwright)
These tools use a headless Chrome instance to generate PDFs. This produces pixel-perfect output, identical to what you see in the browser. Perfect when you need your PDF to look like the webpage (consistent styles, fonts, and layout), and when generating long or multi-page documents.
Pros:
- Pixel perfect
- Handel long pages correctly
Cons:
- Requires a backend
- Not ideal for purely client-side apps
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("https://yourcompany.com", {
waitUntil: "networkidle0",
});
await page.pdf({
path: "page.pdf",
format: "A4",
printBackground: true,
});
await browser.close();
Client side vs Server side print:
- Client side: Generating big PDFs on the renderer thread can freeze the UI, making your app feel janky, especially with long documents or many images.
- Server side: Requires a backend server and not ideal for purely client-side apps.
Recommendations:
Which Library Should You Use?
“I want the PDF to look exactly like my webpage”
Use: Puppeteer or Playwright (Only reliable option)“I need a professional layout (invoice, contract, estimate)”
Use: react-pdf“I already have a PDF template (like a contract) and I just need to fill fields”
Use: pdf-lib“I want a quick HTML → PDF in the browser”
Use: jsPDF (but be ready for CSS issues)“I need millions of PDFs server-side”
Use: Puppeteer + caching or streaming“I need dynamic client-side generation without a backend”
Use: react-pdf or jsPDF
Top comments (0)