Learn how to convert HTML pages to PDF for free. Compare browser methods, online tools, and programmatic approaches for perfect HTML-to-PDF conversion.
You built a beautiful web page. The layout is pixel-perfect, the typography is crisp, the colors are exactly right. Then someone asks you to send it as a PDF.
You hit Ctrl+P, click "Save as PDF," and the result looks like it was designed by someone who has never seen your website. Half the content is cut off mid-sentence. The background colors vanish. That carefully crafted sidebar is now overlapping the main content. Your CSS Grid layout has turned into a vertical stack of sadness.
Converting HTML to PDF should be simple. It is not. But it can be, once you understand why things break and which method fits your specific use case.
This guide covers every approach — from the one-click browser method to full programmatic control. Whether you need to save a single webpage, generate invoices from templates, or batch-convert hundreds of pages, there is a method here that works.
Every modern browser can save a page as PDF through the print dialog. It is the fastest approach and requires zero setup.
How to do it:
When it works well: Simple, text-heavy pages with minimal layout complexity. Blog posts, articles, documentation pages.
When it fails: Anything with complex CSS layouts (Grid, Flexbox), sticky headers, fixed-position elements, background images, or custom fonts that aren't embedded. Single-page apps that rely on JavaScript rendering will often produce blank or incomplete PDFs.
Pro tips for better results:
Browser print-to-PDF is a perfectly valid solution for personal use. But if you need consistent, professional output, keep reading.
Here is where things get interesting. CSS has a built-in mechanism for controlling how pages look when printed — and by extension, when saved as PDF. The @media print query lets you write styles that only apply during print or PDF export.
This is the single most impactful thing you can do to improve HTML-to-PDF output, and most developers never bother with it.
@media print {
/* Hide navigation, footers, ads, and interactive elements */
nav,
footer,
.sidebar,
.cookie-banner,
.social-share,
button,
.no-print {
display: none !important;
}
/* Reset backgrounds and colors for readability */
body {
background: white !important;
color: black !important;
font-size: 12pt;
line-height: 1.5;
}
/* Make links visible in print */
a[href]::after {
content: " (" attr(href) ")";
font-size: 0.9em;
color: #555;
}
/* Prevent internal/anchor links from showing URLs */
a[href^="#"]::after,
a[href^="javascript"]::after {
content: "";
}
/* Ensure images don't overflow pages */
img {
max-width: 100% !important;
page-break-inside: avoid;
}
}Page breaks are what separate a decent PDF from a professional one. Without them, headings end up orphaned at the bottom of a page, tables split in awkward places, and images get sliced in half.
@media print {
/* Always start new sections on a fresh page */
h1 {
page-break-before: always;
}
/* Never break right after a heading */
h1, h2, h3, h4 {
page-break-after: avoid;
break-after: avoid;
}
/* Keep figures, tables, and code blocks together */
figure,
table,
pre,
blockquote {
page-break-inside: avoid;
break-inside: avoid;
}
/* Prevent orphaned lines */
p {
orphans: 3;
widows: 3;
}
}The CSS @page rule gives you control over page margins, size, and — in some renderers — headers and footers.
@page {
size: A4;
margin: 2cm 1.5cm;
}
@page :first {
margin-top: 4cm; /* Extra space on first page for title */
}
/* Named pages for different sections */
@page landscape-section {
size: A4 landscape;
}
.wide-table-section {
page: landscape-section;
}If you already have an HTML page that needs better PDF output, adding a print stylesheet is the fastest path to improvement. Our HTML Formatter can help you clean up your markup before adding print styles, and the CSS Minifier will keep your production print stylesheet lean.
If you need a quick conversion without writing any code, online converters are the pragmatic choice. You paste a URL or upload an HTML file, and the tool generates a PDF.
Our own HTML to PDF converter runs entirely in your browser — your HTML never leaves your machine, which matters if you are working with sensitive content like invoices, contracts, or internal documents.
What to look for in an online converter:
Limitations of online converters:
For one-off conversions of public pages, online tools are perfectly fine. For anything recurring, automated, or high-volume, you need a programmatic approach.
When you need to generate PDFs at scale — invoices, reports, certificates, documentation — you want programmatic control. Two tools dominate this space.
Headless browsers render your HTML exactly as a real browser would, then export the result as PDF. This gives the highest fidelity output because you are literally using a real browser engine.
// Node.js with Puppeteer
const puppeteer = require('puppeteer');
async function htmlToPdf(url, outputPath) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url, {
waitUntil: 'networkidle0' // Wait for all resources to load
});
await page.pdf({
path: outputPath,
format: 'A4',
printBackground: true,
margin: {
top: '1cm',
right: '1.5cm',
bottom: '1cm',
left: '1.5cm'
},
displayHeaderFooter: true,
headerTemplate: '<div style="font-size:9px;width:100%;text-align:center;">My Document</div>',
footerTemplate: '<div style="font-size:9px;width:100%;text-align:center;">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>'
});
await browser.close();
}
htmlToPdf('https://example.com/invoice/123', 'invoice.pdf');When to use headless browsers:
Downsides: Resource-heavy (each conversion spawns a browser instance), slower than dedicated converters (1-5 seconds per page), and requires Node.js infrastructure.
For server environments where spinning up a full browser is overkill, wkhtmltopdf is a command-line tool that converts HTML to PDF using a WebKit rendering engine.
# Basic conversion
wkhtmltopdf https://example.com output.pdf
# With options
wkhtmltopdf \
--page-size A4 \
--margin-top 10mm \
--margin-bottom 10mm \
--margin-left 15mm \
--margin-right 15mm \
--print-media-type \
--enable-local-file-access \
--header-center "Document Title" \
--footer-center "Page [page] of [topage]" \
input.html output.pdf
# Convert multiple files into one PDF
wkhtmltopdf page1.html page2.html page3.html combined.pdfWhen to use wkhtmltopdf:
Downsides: Uses an older WebKit engine, so modern CSS features may not render correctly. JavaScript support is limited. The project is in maintenance mode — no major updates expected.
If you are generating PDFs from data (invoices, reports, receipts), the smartest approach is to design your HTML specifically for PDF output from the start.
<!DOCTYPE html>
<html>
<head>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Helvetica Neue', Arial, sans-serif; }
.page {
width: 210mm;
min-height: 297mm;
padding: 20mm;
margin: 0 auto;
}
.invoice-header {
display: flex;
justify-content: space-between;
margin-bottom: 40px;
border-bottom: 3px solid #2563eb;
padding-bottom: 20px;
}
.invoice-table {
width: 100%;
border-collapse: collapse;
break-inside: avoid;
}
.invoice-table th {
background-color: #f1f5f9;
padding: 10px;
text-align: left;
border-bottom: 2px solid #e2e8f0;
}
.invoice-table td {
padding: 10px;
border-bottom: 1px solid #e2e8f0;
}
.total-row {
font-weight: bold;
font-size: 1.2em;
border-top: 3px solid #2563eb;
}
@media print {
.page { padding: 0; }
.no-print { display: none; }
}
</style>
</head>
<body>
<div class="page">
<!-- Template content here -->
</div>
</body>
</html>Key principles for print-optimized templates:
By default, browsers strip backgrounds when printing. In CSS, force them:
@media print {
body {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
color-adjust: exact !important;
}
}In Puppeteer, set printBackground: true.
Use break-inside: avoid on elements that should not be split. For tables, use thead { display: table-header-group; } to repeat headers on every page.
Embed fonts using @font-face with base64-encoded font data, or use widely available system fonts. Web fonts loaded from CDNs may not be available during server-side conversion.
If your page uses JavaScript to render content, browser print-to-PDF and wkhtmltopdf may produce incomplete results. Use a headless browser (Puppeteer/Playwright) with waitUntil: 'networkidle0' to ensure all async content loads before conversion.
Make sure your <a> tags have full href attributes (not just onclick handlers). Most PDF converters will preserve standard hyperlinks.
| Scenario | Best Method |
|---|---|
| Save a single webpage quickly | Browser Print-to-PDF |
| Improve PDF output of your own site | CSS @media print |
| One-off conversion without coding | Online converter |
| Generate invoices/reports from code | Puppeteer or Playwright |
| Batch convert static HTML on a server | wkhtmltopdf |
| Create PDF-first documents | Print-optimized HTML templates |
There is no single best method — only the right method for your situation. For most web developers, combining CSS print styles with a headless browser gives the best balance of quality and automation.
Once you have your PDFs, you may need to do more with them. Need to combine multiple converted pages into one document? Our Merge PDF tool handles that without uploading your files anywhere. Want to annotate, highlight, or add form fields? The PDF Editor runs entirely in your browser.
The HTML-to-PDF pipeline does not have to be painful. Write clean, semantic HTML. Add a print stylesheet. Pick the right conversion tool for your volume and complexity. And stop settling for PDFs that look like they were generated by a fax machine from 2003.
Your HTML deserves better.