Free tools Windows power users keep installed
One-click scans. No signup required.
The right way to convert HTML to PDF in React depends on what you are exporting. For an existing page that users can print, call window.print() and add print-only CSS. For a client-side download of one DOM element, use html2pdf.js. For invoices or reports that need a dedicated, predictable layout, build a separate document with @react-pdf/renderer. For automated server output from HTML, use Puppeteer and page.pdf(). React’s rendering APIs produce HTML, not PDF files, so they must be combined with one of these browser or PDF-generation workflows.
Choose the conversion route first
Decide whether the PDF should reproduce an existing React view or be authored as a separate document. Also decide where conversion runs and whether a person should control the final save.
| Approach | Best fit | Where it runs | Main trade-off |
|---|---|---|---|
window.print() plus print CSS |
An existing page and a user who can use the print dialog | Browser | Minimal code; browser settings and the user complete the save workflow |
| html2pdf.js | Downloading a selected element or page from the browser | Browser | Convenient options, but layout depends on the html2canvas/jsPDF pipeline |
@react-pdf/renderer |
Invoices, reports, and other PDF-specific documents | Browser or server | You author PDF primitives instead of exporting arbitrary DOM |
Puppeteer Page.pdf() |
Automated HTML-to-PDF generation | Backend with a suitable browser runtime | Strong server control; you operate Chromium automation |
Validate a representative document in the exact browser or runtime you will ship. Documentation describes APIs and options, but cannot guarantee that your application’s fonts, assets, CSS, and page breaks will export correctly.
Method 1: let the browser print the React page
This is the simplest solution when a user can choose “Save to PDF” in the browser’s print dialog. window.print() opens that dialog for the current document; if the page is still loading, the browser finishes loading before showing it. MDN lists the API as widely available across devices and browser versions.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →#1 Best Overall
Add a print button
export default function PrintButton() {
return (
<button type="button" onClick={() => window.print()}>
Print or save as PDF
</button>
);
}
Hide application chrome with print CSS
/* app.css */
@media print {
.no-print,
nav,
button,
.chat-widget {
display: none !important;
}
@page {
size: A4;
margin: 16mm;
}
body {
color: #000;
background: #fff;
font-size: 11pt;
}
a {
color: #000;
text-decoration: none;
}
.page-break-before {
break-before: page;
}
}
Use print media rules to remove navigation and interactive controls, adjust dimensions and colors, and control page breaks. The user still chooses the destination, paper size, margins, scale, backgrounds, and other options in the browser UI. Test with the target browser’s print preview rather than assuming screen CSS will carry over.
Method 2: download an element with html2pdf.js
html2pdf.js documentation describes a browser workflow that selects an element, passes options, and saves a file. It uses html2canvas and jsPDF, so it is an element-conversion pipeline rather than a new PDF document model.
Install and create a download component
npm install html2pdf.js
import html2pdf from 'html2pdf.js';
export function DownloadReport({ report }) {
const download = () => {
const element = document.getElementById('report');
if (!element) return;
const options = {
margin: [10, 10, 10, 10],
filename: `report-${report.id}.pdf`,
image: { type: 'jpeg', quality: 0.95 },
html2canvas: {
scale: 2,
useCORS: true,
backgroundColor: '#ffffff'
},
jsPDF: {
unit: 'mm',
format: 'a4',
orientation: 'portrait'
},
pagebreak: {
mode: ['css', 'legacy']
}
};
html2pdf().set(options).from(element).save();
};
return (
<>
<button type="button" onClick={download}>Download PDF</button>
<article id="report">{report.content}</article>
</>
);
}
The documented options cover margins, filename, image type and quality, html2canvas settings, jsPDF units, paper format, orientation, and page-break modes. Add CSS such as .avoid-break { break-inside: avoid; } to keep cards together where the conversion engine honors it.
What to test
- Long pages and tables that cross page boundaries.
- Web fonts, images, SVG, and cross-origin assets.
- Fixed or sticky elements, transforms, and complex filters.
- Very large DOM trees, which can increase browser memory use.
Because html2canvas first represents the page for jsPDF, the result may differ from native browser printing. Inspect the actual PDF on the browsers and content types your application supports; do not promise pixel-perfect output from the package description alone.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsMethod 3: author a PDF with @react-pdf/renderer
Use @react-pdf/renderer when the PDF is its own document, not a copy of arbitrary existing DOM. Its React primitives include Document, Page, View, and Text, styled with JavaScript objects.
Browser download example
npm install @react-pdf/renderer
import {
Document, Page, Text, View, StyleSheet, PDFDownloadLink
} from '@react-pdf/renderer';
const styles = StyleSheet.create({
page: { padding: 32, fontSize: 11 },
heading: { fontSize: 20, marginBottom: 12 },
row: { marginBottom: 8 }
});
function InvoicePdf({ invoice }) {
return (
<Document>
<Page size="A4" style={styles.page}>
<Text style={styles.heading}>Invoice {invoice.number}</Text>
{invoice.lines.map(line => (
<View style={styles.row} key={line.id}>
<Text>{line.description} — {line.amount}</Text>
</View>
))}
</Page>
</Document>
);
}
export function InvoiceDownload({ invoice }) {
return (
<PDFDownloadLink
document={<InvoicePdf invoice={invoice} />}
fileName={`invoice-${invoice.number}.pdf`}
>
{({ loading }) => loading ? 'Preparing…' : 'Download invoice'}
</PDFDownloadLink>
);
}
This approach gives you a PDF-specific representation that can be displayed or rendered on a server. It does not accept an existing HTML tree unchanged; you must map your data into the library’s document primitives. That extra modeling is worthwhile when stable pagination and a controlled document design matter more than reusing web markup.
Method 4: generate HTML PDFs on the server with Puppeteer
Puppeteer’s Page.pdf() API generates a PDF using print CSS media. Options include paper format, landscape mode, margins, backgrounds, page ranges, CSS page-size preference, scale, and waiting for fonts. The PDF generation guide shows opening a browser, navigating, saving the file, and closing the browser.
Express endpoint example
npm install puppeteer express
import express from 'express';
import puppeteer from 'puppeteer';
const app = express();
app.get('/reports/:id.pdf', async (req, res) => {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto(`https://app.example.com/reports/${req.params.id}`, {
waitUntil: 'networkidle0'
});
await page.emulateMediaType('print');
await page.evaluate(() => document.fonts.ready);
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
preferCSSPageSize: true,
margin: { top: '16mm', right: '16mm', bottom: '16mm', left: '16mm' }
});
res.type('application/pdf').send(pdf);
} finally {
await browser.close();
}
});
app.listen(3000);
Keep this code on a backend that can run the chosen Puppeteer browser. Protect authenticated pages and validate the URL or report identifier instead of accepting arbitrary navigation from a client. Ensure data, images, and fonts are ready before calling page.pdf(); otherwise the file can contain placeholders or missing assets. Use CSS @page rules together with preferCSSPageSize when the document defines its own paper size.
Rank #3
Why React rendering APIs do not create PDFs
React’s server APIs are HTML-producing steps. The renderToString reference states that “renderToString renders a React tree to an HTML string.” It has limited Suspense support, does not stream or wait for data, and is not recommended for client-side use. renderToStaticMarkup also returns HTML, but its output is non-interactive and cannot be hydrated. You can feed either result into a browser such as Puppeteer, but another PDF step remains necessary.
Reliability checklist before shipping
- Choose a representative document containing your longest text, largest table, images, and custom fonts.
- Wait for data, images, and
document.fonts.readybefore capture. - Define print colors, margins, paper size, and break rules explicitly.
- Check links, selectable text, page count, headers, footers, and clipped content in the produced PDF.
- Run the test in the production browser or server runtime, not only in a development tab.
- Decide how private data is handled: browser-only methods keep content local, while server rendering sends it to your backend.
Troubleshooting common failures
The PDF is blank
Confirm that the target element exists, the route finished loading, and the browser has reached the intended state. In Puppeteer, inspect the response and wait for the relevant selector or network activity rather than relying on a fixed delay.
Fonts or images are missing
Use accessible asset URLs, wait for fonts and images, and configure cross-origin access where required by a client-side canvas pipeline. Verify that the server can reach every asset when using Puppeteer.
Content is cut off or overlaps
Replace screen-only fixed heights with content-driven sizing, add print break rules, and test tables and flex layouts across page boundaries. For a complex report, a PDF-specific renderer may be more predictable than converting arbitrary DOM.
Recommended Free Tools
Rank #4
Styles look different
Browser printing, html2pdf.js, and Puppeteer do not share identical rendering paths. Put print-specific rules in @media print, enable backgrounds where appropriate, and compare output in the exact target runtime.
The browser freezes during client conversion
Reduce the captured DOM, lower canvas scale or image quality, and split very large documents. Move conversion to a server workflow when browser memory is insufficient.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
ScreenshotNeo is a website screenshot API that can return a PDF from one GET request. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server includes take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
See the ScreenshotNeo API documentation for all options, including full-page capture, CSS-selector elements, print settings, custom CSS and JavaScript, waits, headers, cookies, user agents, authorization, timezone, geolocation, caching, signed links, asynchronous webhooks, bulk capture, and usage reporting.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
The Free plan includes 1,000 shots each month with no card; paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Create a free ScreenshotNeo account.
Best Value
Which method should you use?
- Choose browser printing when a person can review and save the current page.
- Choose html2pdf.js for a client-side button that converts a bounded element.
- Choose @react-pdf/renderer when the PDF is a separately designed business document.
- Choose Puppeteer when your backend must generate PDFs automatically from HTML.
Frequently Asked Questions
Can I convert a React component directly with renderToString?
No. It returns an HTML string. Pass that HTML to a browser print or PDF pipeline such as Puppeteer, or use a PDF-specific renderer.
Is browser printing suitable for unattended downloads?
No. It depends on the user’s print dialog. Use html2pdf.js for a browser-controlled download or Puppeteer for an automated backend file.
Should an invoice use html2pdf.js or react-pdf?
Use react-pdf when you can model the invoice as a dedicated document and need predictable PDF structure; use html2pdf.js when reusing the existing invoice DOM is the priority.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →How do I verify page breaks?
Export a representative long document in the production runtime and inspect tables, images, fonts, clipping, and break rules in the resulting PDF.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




