Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchChoose the conversion route that matches your output. Use window.print() with print CSS when users can save the current React page through the browser dialog. Use html2pdf.js when you need a client-side download of an existing element. Use @react-pdf/renderer when the PDF can have its own document layout, and use Puppeteer when a server must generate PDFs automatically from HTML.
React’s rendering APIs produce HTML, not PDF files. renderToString returns an HTML string, while renderToStaticMarkup produces non-interactive HTML. A browser print pipeline or a PDF library must perform the final conversion.
Pick the right React-to-PDF approach
Decide two things first: are you exporting an existing DOM tree or authoring a separate PDF document, and should conversion happen in the browser or on a server?
| Approach | Best fit | Where it runs | Main trade-off |
|---|---|---|---|
window.print() plus print CSS |
An existing page that users can print or save | Browser print dialog | Minimal code, but the user and browser complete the save workflow |
html2pdf.js |
Downloading one existing element or page in the browser | Client browser | Convenient options, but layout depends on the html2canvas/jsPDF pipeline |
@react-pdf/renderer |
Invoices, reports, and other purpose-built documents | Browser or server | You author PDF primitives instead of exporting arbitrary DOM |
Puppeteer page.pdf() |
Automated, server-side HTML-to-PDF generation | Backend with a suitable browser runtime | Strong control over print settings, with browser operations to run and maintain |
Whichever route you choose, test a representative document in the exact browser or runtime you will ship. Documentation describes APIs and options; it cannot guarantee that your particular fonts, images, CSS, asynchronous data, or page breaks will export correctly.
#1 Best Overall
Option 1: let users print the React page
Printing is the simplest solution when a person can click a button, review the browser preview, and choose “Save as PDF.” MDN documents window.print() as widely available; the browser finishes loading the document before opening the dialog. Add print-only CSS to remove navigation and controls and to make the page paper-friendly.
1. Add a print button
export function PrintButton() {
return (
<button type="button" onClick={() => window.print()}>
Save as PDF
</button>
);
}
Call it only in response to a clear user action. Do not call it during render or an effect, which can open a dialog unexpectedly.
2. Define print media rules
/* app.css */
@page {
size: A4;
margin: 16mm;
}
@media print {
.site-header,
.site-nav,
.print-button,
.no-print {
display: none !important;
}
.invoice {
max-width: none;
color: #000;
background: #fff;
}
a {
color: #000;
text-decoration: none;
}
.avoid-break {
break-inside: avoid;
}
h1, h2, h3 {
break-after: avoid;
}
}
The MDN printing guide covers print media queries and print-specific styling. Preview the page, select the intended paper size, margins, scale, backgrounds, headers, and footers, then save from the browser’s print UI. Browser settings can override parts of your CSS, so document the expected settings if consistent output matters.
Option 2: download an element with html2pdf.js
html2pdf.js is designed for client-side conversion of an existing page or selected element. Its pipeline uses html2canvas and jsPDF and exposes controls for margins, filename, image type and quality, paper format, orientation, and page-break behavior.
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 →Install and mark the export area
npm install html2pdf.js
import html2pdf from 'html2pdf.js';
export function Report({ report }) {
const downloadPdf = async () => {
const element = document.getElementById('report-to-export');
if (!element) throw new Error('Export element was not found');
await html2pdf()
.set({
margin: [12, 12, 12, 12],
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'] }
})
.from(element)
.save();
};
return (
<>
<button type="button" onClick={downloadPdf}>Download PDF</button>
<article id="report-to-export">{/* report content */}</article>
</>
);
}
Make the conversion predictable
- Wait until data, images, and web fonts have loaded before calling the converter. A button disabled until the report is ready is safer than a fixed delay.
- Use explicit dimensions and CSS break rules for sections that must stay together. Very long elements and complex positioning need testing because the canvas-to-PDF pipeline does not behave exactly like native browser printing.
- For cross-origin images, configure the image host correctly and test with production URLs. A canvas security restriction or an image that loads after capture can produce missing content.
- Choose JPEG for smaller photographic output or PNG when sharp text and transparency matter; verify the resulting file size and readability.
This route is a conversion of DOM, not a new PDF layout system. If the document needs a completely different structure from the screen, use React PDF primitives instead.
Option 3: author a PDF with @react-pdf/renderer
@react-pdf/renderer provides Document, Page, View, and Text primitives with style objects. It is a good fit when the PDF is a designed document such as an invoice or report and does not need to reproduce arbitrary HTML.
Rank #3
Install and build a document component
npm install @react-pdf/renderer
import {
Document,
Page,
Text,
View,
StyleSheet,
PDFDownloadLink
} from '@react-pdf/renderer';
const styles = StyleSheet.create({
page: { padding: 36, fontSize: 11 },
heading: { fontSize: 20, marginBottom: 18 },
row: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 8 }
});
function InvoicePdf({ invoice }) {
return (
<Document>
<Page size="A4" style={styles.page}>
<Text style={styles.heading}>Invoice {invoice.number}</Text>
{invoice.items.map((item) => (
<View style={styles.row} key={item.id}>
<Text>{item.description}</Text>
<Text>{item.total}</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>
);
}
Because this document has its own component tree and styles, changes to your HTML page do not automatically appear in the PDF. That separation is useful: pagination and typography are designed for the document rather than inherited from a responsive screen. The package also documents browser display and server-rendering options.
Option 4: generate HTML PDFs on a server with Puppeteer
Use Puppeteer when a backend must create files without a user opening a print dialog. Page.pdf() renders with the print CSS media type and supports paper format, orientation, margins, backgrounds, page ranges, CSS page-size preference, scale, and waiting for fonts. The PDF generation guide shows the browser, navigation, PDF, and close sequence.
Install Puppeteer
npm install puppeteer
Create a PDF endpoint
import express from 'express';
import puppeteer from 'puppeteer';
const app = express();
app.get('/reports/:id.pdf', async (req, res, next) => {
let browser;
try {
browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(`https://app.example.com/reports/${req.params.id}`, {
waitUntil: 'networkidle0'
});
await page.emulateMediaType('print');
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
preferCSSPageSize: true,
margin: { top: '16mm', right: '16mm', bottom: '16mm', left: '16mm' },
waitForFonts: true
});
res.type('application/pdf').send(pdf);
} catch (error) {
next(error);
} finally {
if (browser) await browser.close();
}
});
app.listen(3000);
The page must be reachable from the backend and contain the data you intend to export. For authenticated applications, arrange an appropriate server-side session or a dedicated protected route; do not expose credentials in a client bundle. Keep browser startup and cleanup inside an operational design that suits your hosting environment, and test concurrency rather than assuming desktop-browser behavior.
Rank #4
Control pagination with CSS
@page { size: Letter; margin: 0.6in; }
@media print {
.section { break-inside: avoid; }
.page-break-before { break-before: page; }
.report-table thead { display: table-header-group; }
}
Use Puppeteer’s landscape, scale, pageRanges, and explicit margin options when the document requires them. If CSS should determine the paper size, enable preferCSSPageSize. Set printBackground when colored backgrounds are part of the design.
Why React server rendering alone cannot make a PDF
renderToString “renders a React tree to an HTML string,” according to the React documentation. It does not create a PDF file, does not stream or wait for data, and sends fallback markup immediately if a component suspends. React does not recommend it for client-side use. renderToStaticMarkup also creates HTML, but the result is non-interactive and cannot be hydrated; React describes it as useful for static output such as emails. Either API can feed a server HTML pipeline, after which a browser engine such as Puppeteer must perform PDF generation.
Troubleshooting checklist
The PDF is blank or missing data
- Cause: capture starts before asynchronous data, images, or fonts are ready.
- Fix: disable the download control until the view is ready; in Puppeteer, navigate with an appropriate wait condition and use
waitForFonts. Verify the final DOM before capture.
Styles look different from the screen
- Cause: print media rules, browser print settings, or unsupported canvas/layout behavior.
- Fix: inspect the page in print emulation, add an explicit
@media printstylesheet, and remove screen-only controls. For html2pdf.js, simplify problematic positioning and test the actual content.
Images disappear
- Cause: an image is cross-origin, blocked, lazy-loaded, or not complete at capture time.
- Fix: ensure the image host permits the required browser request, wait for images to finish, and test production URLs rather than only local assets.
Pages break in the wrong places
- Cause: content is taller than the available paper area or lacks break rules.
- Fix: apply
break-inside: avoidto atomic sections,break-before: pagewhere a new page is intentional, and choose paper size and margins explicitly. Validate long tables and headings.
The server works locally but fails in deployment
- Cause: the deployed environment cannot launch or access the required browser runtime, URL, fonts, or assets.
- Fix: verify browser availability, outbound access, certificates, resource URLs, and cleanup behavior in the target runtime. Log navigation and PDF errors separately so a failed page load is not mistaken for a PDF formatting problem.
Performance, reliability, and privacy decisions
- User-controlled print: avoids a server PDF job and is usually the least code, but output depends on browser settings and requires a person to save the file.
- Client conversion: keeps content in the browser, but large DOM trees consume memory and can expose layout differences across browsers. Test long documents on the least powerful supported device.
- React PDF: gives a controlled document model, but every field and layout rule must be represented in the PDF component.
- Server conversion: centralizes output and automation, but the page, data, fonts, and assets may cross a server boundary. Define retention and access controls for sensitive documents.
There is no universal winner. Choose existing DOM versus dedicated layout first, then browser versus server execution, and finally the degree of control required over paper format, margins, page breaks, and unattended generation.
Best Value
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server that can return a screenshot or PDF from one GET request. It is useful when your React app is already deployed as a URL and you want a hosted capture step rather than maintaining browser automation. Before capture, it accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and each response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
For a direct request, see the ScreenshotNeo documentation:
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}`);
Configure PDF output, paper size, margins, and other capture options in the API documentation. ScreenshotNeo includes full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, custom CSS and JavaScript, clicks before capture, waits for selectors or network idle, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs are accepted to ease migration.
The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; Growth is $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing provides two months free, and every feature is available on every plan. Create a free ScreenshotNeo account to start with 1,000 screenshots a month and no card.
Frequently Asked Questions
Can I use the same print stylesheet for browser printing and Puppeteer?
Yes. Puppeteer’s PDF method uses the print CSS media type, so a shared @media print stylesheet is a useful baseline. Still verify paper size, margins, fonts, and page breaks in the server runtime.
When should an export be a separate React component?
Use a separate PDF component when the document’s hierarchy, pagination, or typography differs materially from the screen. That is the architectural model provided by @react-pdf/renderer, rather than a DOM snapshot.
What should I test before shipping PDF export?
Test a long document, missing and slow assets, custom fonts, tables spanning pages, print backgrounds, narrow and wide content, authenticated data, and the exact browsers or server runtime your users will use.
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.

