The quickest way to turn a web page into a PDF is the browser's own print dialog: open it, pick the PDF destination and save. To control how that PDF looks, add print CSS with @media print and @page for paper size and margins. For PDFs made automatically, render the page with a headless browser such as Puppeteer or Playwright.
| Method | Runs where | Text selectable | Best for |
|---|---|---|---|
| Print dialog, save as PDF | Any desktop browser | Yes | One-off copies of a page |
| window.print() button | The visitor's browser | Yes | Invoices, receipts, tickets |
| Puppeteer page.pdf() | Node.js driving Chrome | Yes | Automated reports on a server |
| Playwright page.pdf() | A Playwright script driving a browser | Yes | Automated PDFs inside an existing test setup |
| html2pdf.js | The visitor's browser | No, the page becomes an image | Quick client-side downloads |
| jsPDF | The visitor's browser | Yes, for text you draw | Building a PDF from data in code |
Updated
Every method on this page ends up asking a browser engine to lay the page out on paper, so good print CSS improves all of them. Build a starting print stylesheet below, then pick the conversion route that matches who presses the button: a person, your page, or a server.
@page {
size: A4 portrait;
margin: 15mm;
}
@media print {
body {
font-size: 12pt;
color: #000;
background: #fff;
}
nav, header nav, footer, aside, .no-print {
display: none !important;
}
table, figure, pre, img, blockquote {
break-inside: avoid;
}
h1, h2, h3 {
break-after: avoid;
}
}| CSS | Values | Use |
|---|---|---|
| @media print { } | Any rules | Styles that apply only when printing or saving to PDF |
| @page { size } | A5, A4, A3, B5, B4, letter, legal, ledger or lengths; portrait or landscape | Paper size and orientation |
| @page { margin } | Lengths | Margins of every page |
| @page :first | Pseudo-class | Different margins on the first page |
| break-inside | auto, avoid, avoid-page, avoid-column | Keep a block on one page |
| break-before / break-after | auto, avoid, page, left, right | Force or prevent a break at a block |
| print-color-adjust | economy, exact | Whether the browser may drop colours and backgrounds |
window.print() opens the print dialog and blocks the page until it closes. The class keeps the button itself off the paper.
<button type="button" class="no-print" onclick="window.print()">Print or save as PDF</button>
<style>
@media print { .no-print { display: none } }
</style>break-after: page after each invoice gives one invoice per sheet when several are printed together.
@page { size: A4; margin: 18mm 15mm; }
@page :first { margin-top: 25mm; }
@media print {
.invoice { break-after: page; }
.invoice:last-child { break-after: auto; }
.invoice table { width: 100%; border-collapse: collapse; }
.invoice tr { break-inside: avoid; }
}beforeprint and afterprint fire around the dialog, which is useful for expanding collapsed sections.
window.addEventListener("beforeprint", () => {
document.querySelectorAll("details").forEach((d) => d.setAttribute("open", ""));
});
window.addEventListener("afterprint", () => {
console.log("Print dialog closed");
});preferCSSPageSize lets your @page size win over the format option; printBackground keeps backgrounds.
import puppeteer from "puppeteer";
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("https://example.com/invoice/42", { waitUntil: "networkidle0" });
await page.pdf({
path: "invoice-42.pdf",
printBackground: true,
preferCSSPageSize: true,
});
await browser.close();
A @media print block lets you restyle the page for paper without touching the screen design. Hide what makes no sense on paper, widen the content column, and set a readable font size.
The @page rule controls the page box itself: size, margins and orientation, with :first, :left and :right to treat specific pages differently. MDN lists @page as Baseline newly available since December 2024, with some parts still varying between browsers.
If PDFs must be produced without a person, such as a nightly report or an emailed invoice, run a headless browser. Puppeteer's page.pdf() prints with the print media type, takes options such as format, margin and printBackground, and can save straight to a file path.
Server rendering gives the same result every time because you control the browser version and fonts. It also means running and securing that browser, so only take it on when a print button is not enough.
Open the browser's print dialog from its menu or keyboard shortcut, then choose the PDF option in the destination or printer list. Its exact name depends on the browser and operating system.
The simplest honest version is a button that calls window.print(), which opens the same dialog, where the visitor can save as PDF. It needs no library, keeps text selectable and uses your print CSS.
Add break-inside: avoid to the table, or to its rows if the table is long. The older page-break-inside: avoid is treated as an alias, so it still works in older stylesheets.
Yes. @page { size: A4 portrait; margin: 15mm; } sets the size and margins of the page box. Puppeteer only uses it when preferCSSPageSize is true; otherwise its own format option, Letter by default, decides.
Its README explains that it renders the content into an image and places that image in the PDF, so the text is not selectable or searchable and files get large. Use printing or a headless browser when the text matters.
Related: Print HTML tool, HTML viewer, CSS print styles, HTML table borders, HTML and CSS cheat sheet.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.