React has no single PDF-export command. Choose the renderer that matches your control boundary: use browser print when people should choose paper size and destination; use react-to-print to print one component; use a client-side PDF library for a direct download of a small, bounded view; and use Puppeteer with Chromium when a server must create repeatable PDFs automatically.
The reliable pattern is the same in every case: render a dedicated printable view, resolve its data, load fonts and images, apply print CSS, then generate the PDF. The sections below show each path, its limits, and working code.
Choose the PDF path first
| Requirement | Best fit | What the reader gets |
|---|---|---|
| A person should review settings and save or print | Browser print | Native preview, destination, paper, margins and scale controls |
| Print one React component rather than the whole application | react-to-print | A print iframe containing the referenced component, followed by the normal print dialog |
| Immediate client-side download of a small, controlled document | Canvas/PDF library | A file generated in the browser; layout and text selection require validation |
| Automated, authenticated or server-side output | Puppeteer | Repeatable Chromium PDF generation with programmable page settings |
Think in terms of control, not framework. React supplies the view; the browser, a PDF library or Chromium performs the actual rendering.
Build a printable React view
Do not send an entire dashboard to the printer. Create a stable subtree containing the report title, data, tables, images and footnotes. Keep navigation, filters, export buttons and live widgets outside it.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
#1 Best Overall
export function Invoice({ invoice }) {
return (
<main className="printable-invoice">
<h1>Invoice {invoice.number}</h1>
<p>Issued {invoice.issuedAt}</p>
<table>
<tbody>
{invoice.lines.map(line => (
<tr key={line.id}>
<td>{line.description}</td>
<td>{line.total}</td>
</tr>
))}
</tbody>
</table>
</main>
);
}
Keep values formatted for paper (for example, currency and dates), provide meaningful headings, and use real table headers for accessibility. A printable route is often easier to test than conditionally hiding half of an interactive screen.
Print CSS that controls paper layout
@page {
size: A4;
margin: 16mm;
}
@media print {
.app-nav, .filters, .export-button { display: none !important; }
.printable-invoice { color: #000; background: #fff; }
a { color: inherit; text-decoration: none; }
.avoid-break { break-inside: avoid; }
h1, h2 { break-after: avoid; }
thead { display: table-header-group; }
tr { break-inside: avoid; }
}
/* Needed when a Chromium PDF must preserve design colors */
.printable-invoice { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
@page supplies a default paper size and margin, while the user or automation environment can still override settings. Test long tables, repeated headers, images, custom fonts and page breaks in the browsers you support.
Option 1: browser print for user-controlled PDFs
This is the smallest implementation and gives the user the browser’s preview and destination controls.
- Render the printable subtree and wait until its data, images and fonts are ready.
- Apply the print rules above.
- Call
window.print()from a user gesture such as a button click. - In the preview, select “Save to PDF” (the exact label varies by browser and operating system).
export function PrintButton() {
return (
<button type="button" onClick={() => window.print()}>
Print or save PDF
</button>
);
}
There is no portable JavaScript API that silently chooses a filename, destination or paper settings for every browser. If those choices must be automatic, move generation to a controlled Chromium process.
Option 2: print one component with react-to-print
react-to-print copies the component referenced by a ref into a print iframe and opens the normal print flow. Its documentTitle option helps browsers suggest a filename, and fonts can load fonts into the iframe. It does not itself save a PDF after preview opens.
import { useRef } from 'react';
import { useReactToPrint } from 'react-to-print';
export function ReportPage({ report }) {
const contentRef = useRef(null);
const print = useReactToPrint({
contentRef,
documentTitle: `report-${report.id}`,
fonts: [
{ family: 'Inter', source: '/fonts/inter-regular.woff2' }
]
});
return (
<>
<button type="button" onClick={() => print()}>Print report</button>
<section ref={contentRef} className="printable-invoice">
<h1>{report.title}</h1>
{/* report content */}
</section>
</>
);
}
The browser still controls margins, paper and the final destination. A custom print callback can receive the iframe and pass it to another generator, but that is an integration you own rather than a built-in silent download. Check mobile browsers and embedded WebViews separately because print support differs.
Option 3: client-side PDF generation
Canvas/PDF libraries can capture a bounded DOM region and trigger a download without opening a print dialog. This is useful for a short invoice, certificate or snapshot where the browser is the only runtime. It is a poor default for complex reports: rasterized text may not be selectable, very long content needs manual pagination, and advanced CSS, sticky elements, SVG, fonts and cross-origin images can render differently.
- Capture a fixed, visible container rather than an unbounded scrolling application.
- Wait for images and
document.fonts.readybefore capture. - Provide explicit page width, height and margins in the PDF library.
- Compare output in target browsers and inspect text selection, table splits and image quality.
If accessibility, searchable text or precise pagination matters, prefer browser print or server-side Chromium.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Option 4: automated PDFs with Puppeteer
Puppeteer is the practical choice for a server route, scheduled report or batch export. Load a rendered React URL in headless Chromium, wait for application data and assets, then call page.pdf(). Puppeteer documents: “For printing PDFs use Page.pdf().” PDF generation uses print media by default and waits for fonts.
import puppeteer from 'puppeteer';
export async function createPdf(url, outputPath, token) {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.setExtraHTTPHeaders({
Authorization: `Bearer ${token}`
});
await page.goto(url, { waitUntil: 'networkidle2' });
// Wait for your React route's data, not merely for the first HTML shell.
await page.waitForSelector('[data-pdf-ready="true"]');
await page.evaluate(() => document.fonts.ready);
await page.pdf({
path: outputPath,
format: 'A4',
printBackground: true,
preferCSSPageSize: true
});
} finally {
await browser.close();
}
}
createPdf('https://example.com/reports/42/print', './report.pdf', process.env.REPORT_TOKEN);
Set data-pdf-ready="true" only after the React component has fetched its data and completed any chart rendering. Keep the print CSS in the same codebase as the route. By default, Puppeteer uses print media; call await page.emulateMediaType('screen') only when the PDF intentionally follows screen styles. Use printBackground: true and the print-color-adjust rule when colored backgrounds are part of the design.
Authentication and isolation
Expose a dedicated export route protected by a short-lived token or server-side session. Never place a user’s long-lived credential in a public URL. Create a fresh page per job, restrict navigation to approved origins, and close the browser even when generation fails. Remove secrets from logs and from generated HTML.
React server rendering and data timing
If a PDF route renders React outside a browser, renderToString converts a tree to HTML immediately; it does not wait for data. Fetch and validate all required records first, then render the tree and send the resulting HTML to Chromium or another HTML-to-PDF renderer. A loading spinner captured by the browser is usually a data-timing bug, not a PDF bug.
Fonts, images and pagination
- Fonts: wait for
document.fonts.ready; ensure the font files are reachable from the export origin. A fallback font changes line wrapping and page count. - Images: use absolute or same-origin URLs where possible, wait for
img.decode(), and configure CORS when a client-side canvas reads pixels. - Charts: prefer SVG or a fully rendered canvas; wait for the chart library’s completion signal.
- Tables: repeat headers with
thead { display: table-header-group; }and avoid breaking rows. Extremely wide tables may need a landscape page or a separate layout. - Page breaks: use
break-before,break-afterandbreak-inside; treat them as hints and test real content because the layout engine can still move oversized elements.
Performance, reliability and cost decisions
Browser print shifts CPU and storage to the user and is usually adequate for occasional exports. Client-side capture avoids a server browser but can consume substantial memory for large DOM trees. Puppeteer costs server CPU and browser startup time, yet gives deterministic settings and works for scheduled or multi-user jobs. Reuse a controlled browser process for throughput, create isolated pages per request, cap concurrent jobs, and set navigation and generation timeouts.
Cache immutable assets and data, but do not cache a personalized PDF under a shared key. For a large report, generate asynchronously and return a job identifier rather than keeping an HTTP request open. Record the route, data version, browser version and failure reason so a missing font or API timeout can be diagnosed.
Troubleshooting common failures
The PDF is blank or shows a loading spinner
Your capture ran before React data resolved. Add a deterministic ready marker, wait for it in Puppeteer, and verify the API request succeeded in the export session.
Styles look like the screen or colors disappeared
Print media is active. Move required rules into @media print, use printBackground: true for Puppeteer, and apply print-color-adjust: exact where preserving color is intentional.
The filename is ignored
Browsers can override suggested names. documentTitle helps with react-to-print, but only a server response or download you control can consistently set a filename.
Custom fonts change wrapping
The font was not loaded in the print context. Supply it to react-to-print, make the font URL accessible, and await document.fonts.ready before generating.
Images or canvas content is missing
Wait for image decoding and chart completion. For canvas capture, fix cross-origin permissions; a tainted canvas cannot be read by client-side PDF code.
Puppeteer times out at network idle
Analytics, WebSockets or long polling can prevent an idle state. Use a focused readiness selector, wait for the specific data request or add a bounded delay after the application signals completion.
Pages split awkwardly
Add print-only wrappers with break-inside: avoid, repeat table headers, and provide a landscape or condensed print layout for wide content. Validate with the longest realistic records, not only a short fixture.
Or skip the browser setup: ScreenshotNeo
For a hosted PDF of a rendered React route, ScreenshotNeo provides a single API endpoint and an MCP server. Its PDF capture supports paper size, margins, landscape mode and page ranges, along with waits for a selector, delay or network idle. You can also send custom headers, cookies, a user agent and Authorization for a protected export route.
Rank #4
Use the API after your React route has its own loading and print CSS. The response is a PDF when you request PDF output; the same service can capture PNG, JPEG or WebP screenshots.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for PDF parameters and output options. Equivalent requests in Python and Node.js are:
Recommended Free Tools
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}`);
Before capture, ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP tools—take_screenshot, get_page_info and capture_pdf—let Claude, Cursor and other MCP clients request captures. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
FAQ
Can React export a PDF without a library?
Yes. Render a printable view and call window.print(); the browser’s preview can save it as PDF.
Is react-to-print a PDF generator?
It prepares one component for the browser print flow. It does not silently download a PDF by itself.
Should a public export URL be indexed?
Usually no. Protect personalized routes and send appropriate authentication and indexing controls; an export endpoint can contain private report data.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Which approach preserves selectable text best?
Browser or Chromium printing generally preserves document text better than a rasterized canvas capture, provided the content is rendered as HTML text.
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.

