Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

There is no single “React to PDF” library for every job. Use react-to-print when a user should print a component and choose “Save as PDF”; use html2pdf.js when the conversion must happen in the user’s browser; and use Puppeteer or Playwright when a server or automated worker must produce a repeatable PDF. The key differences are who controls rendering, whether text stays selectable, how closely print CSS is followed, and where the browser runtime is hosted.

Choose the rendering model before choosing a package

“Convert a React component to PDF” describes several different workflows. A print button, an offline browser export and an automated invoice service have different requirements.

Requirement Best starting point Main trade-off
Let a user print selected React UI and choose a destination such as Save as PDF react-to-print The browser owns the print dialog and settings. The library does not directly download a PDF by itself.
Create a PDF from an element in the user’s browser html2pdf.js Convenient and client-side, but it rasterizes the rendered page; text may not remain selectable or searchable and files can be larger.
Generate PDFs automatically from rendered HTML Puppeteer or Playwright Requires a headless-browser runtime and operational work. PDFs use print CSS media by default.

For long, data-heavy documents where users need search, copy and accessibility, treat image-based output as a serious limitation. For deterministic delivery—such as emailing an invoice after an order—run a browser automation library in a controlled server or job worker. Browser behavior and package APIs change, so test the exact browsers and versions you support.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Option 1: print a React component with react-to-print

react-to-print prepares the content of a selected component and invokes the native browser print flow. The user still sees print preview and chooses “Save as PDF”; the package does not silently download a PDF without that preview. Its custom print hook can hand the prepared content to another generator if your application needs a different output path.

Install and create a printable component

npm install react-to-print
import { useRef } from "react";
import { useReactToPrint } from "react-to-print";

export default function InvoicePage({ invoice }) {
  const printRef = useRef(null);

  const printInvoice = useReactToPrint({
    contentRef: printRef,
    documentTitle: `invoice-${invoice.number}`,
    onPrintError: (location, error) => {
      console.error(`Print failed during ${location}`, error);
    }
  });

  return (
    <>
      <button type="button" onClick={printInvoice}>Print / Save PDF</button>
      <main ref={printRef} className="invoice">
        <h1>Invoice {invoice.number}</h1>
        <p>Customer: {invoice.customer}</p>
        <ol>
          {invoice.items.map((item) => (
            <li key={item.id}>{item.description}: {item.total}</li>
          ))}
        </ol>
      </main>
    </>
  );
}

Use the ref on the actual node you want copied into the print document. Print-specific CSS should target that node and its descendants; ancestor styles outside the printed subtree may not be present.

@media print {
  body { margin: 0; }
  .invoice { color: #000; background: #fff; padding: 12mm; }
  .no-print, button { display: none !important; }
  .invoice { break-inside: avoid; }
}

What this route can and cannot control

  • The user’s browser controls paper size, margins, headers and footers, scaling and destination. JavaScript cannot reliably set those print-dialog choices through window.print.
  • Fonts, images and asynchronous data must be ready before printing. Render loading states only after the data has arrived.
  • Most modern desktop browsers are supported, but project documentation calls out limitations in mobile WebViews and Firefox for Android. Test every target environment, especially an embedded WebView.
  • Because the browser owns the final print operation, this is usually the simplest and most user-friendly choice for an interactive “print this view” button.

Option 2: create a client-side file with html2pdf.js

html2pdf.js converts a webpage or selected element in the browser using html2canvas and jsPDF. It is browser-only; it does not run in Node.js. The DOM is rendered to a canvas and placed into a PDF, so the result can lose selectable/searchable text and may be larger than a PDF generated from real text and vector layout.

Install and export an element

npm install html2pdf.js
import html2pdf from "html2pdf.js";

export async function downloadReceipt(element) {
  if (!element) throw new Error("Receipt element is not available");

  const options = {
    margin: [10, 10, 10, 10],
    filename: "receipt.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"] }
  };

  await html2pdf().set(options).from(element).save();
}

In a component, attach a ref and call the function from a click handler:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { useRef } from "react";
import { downloadReceipt } from "./downloadReceipt";

export function Receipt({ order }) {
  const receiptRef = useRef(null);
  return (
    <>
      <button onClick={() => downloadReceipt(receiptRef.current)}>
        Download PDF
      </button>
      <article ref={receiptRef}>{order.summary}</article>
    </>
  );
}

Important client-side edge cases

  • Cross-origin images may be omitted or taint the canvas unless the image server supplies appropriate CORS headers and the request is configured for CORS.
  • Very tall pages consume substantial memory because the rendered bitmap must exist in the browser. Split large reports or move generation server-side.
  • Use CSS page-break rules and the library’s page-break options, then inspect several pages. A canvas snapshot cannot reproduce every browser layout feature.
  • Do not call this code during server-side rendering. Import and execute it only in a browser event or a client-only module.
  • Choose JPEG only when its compression artifacts are acceptable. Text-heavy documents generally benefit from a high-quality setting, but the fundamental output remains image-based.

Option 3: generate PDFs with Puppeteer

Puppeteer drives a Chromium browser and exposes page.pdf(). This is suitable for an API endpoint, queue worker or build process that loads a URL or HTML template and returns a file. PDF generation uses print CSS media. If your design is written for screen media, emulate screen media before calling page.pdf().

Minimal Node.js service

import express from "express";
import puppeteer from "puppeteer";

const app = express();
app.use(express.json());

app.post("/pdf", async (req, res) => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.setViewport({ width: 1280, height: 900, deviceScaleFactor: 1 });
    await page.goto(req.body.url, { waitUntil: "networkidle0" });
    await page.emulateMediaType("print");
    const pdf = await page.pdf({
      format: "A4",
      printBackground: true,
      margin: { top: "12mm", right: "12mm", bottom: "12mm", left: "12mm" },
      preferCSSPageSize: true
    });
    res.type("application/pdf").send(pdf);
  } catch (error) {
    res.status(500).json({ error: "PDF generation failed" });
  } finally {
    await browser.close();
  }
});

app.listen(3000);

In production, launch one browser per worker or reuse a managed browser rather than launching a new process for every request. Limit concurrent pages, set navigation and generation timeouts, authenticate private pages safely, and close pages in a finally block. Never pass arbitrary user-supplied URLs to a server browser without SSRF protection.

Print CSS and readiness

Use @media print and @page for paper layout. Wait for application data and fonts before generating. A reliable pattern is to render a marker such as data-pdf-ready="true", then wait for that selector from Puppeteer. Network-idle alone does not prove that a React effect or chart has finished.

Option 4: generate PDFs with Playwright

Playwright offers Chromium, Firefox and WebKit automation and a page PDF method. Like Puppeteer, it creates PDFs with print CSS media. Its browser-context model is useful when each job needs isolated cookies, permissions or locale settings.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { chromium } from "playwright";

const browser = await chromium.launch();
const context = await browser.newContext({ locale: "en-US" });
const page = await context.newPage();
try {
  await page.goto("https://example.com/report", { waitUntil: "networkidle" });
  await page.waitForSelector('[data-pdf-ready="true"]');
  await page.pdf({
    path: "report.pdf",
    format: "A4",
    printBackground: true,
    preferCSSPageSize: true,
    margin: { top: "12mm", right: "12mm", bottom: "12mm", left: "12mm" }
  });
} finally {
  await context.close();
  await browser.close();
}

Install the package and the browser binaries required by your chosen Playwright version. In containers, verify sandbox permissions, installed fonts and shared libraries; a locally working script can fail in a minimal image because Chromium cannot start or a font is missing.

How to decide between the four approaches

Choose react-to-print when the user should remain in control

This is the right fit for a print button, receipts or reports where users expect the familiar browser preview and may change destination or paper settings. It avoids shipping a PDF engine, but it cannot guarantee a silent download or identical output across devices.

Choose html2pdf.js for small, offline-friendly exports

Use it when the data is already in the browser, no server browser is available, and an image-based PDF is acceptable. Confirm that users do not need to search or copy text and watch memory use for long documents.

Choose Puppeteer or Playwright for automated delivery

Use a headless browser when PDFs must be generated on demand without user interaction, attached to email, archived or produced by a queue. Build for browser startup cost, concurrency, timeouts, fonts, authentication and security. Puppeteer centers on Chromium; Playwright can automate multiple browser engines, although PDF output still depends on the browser used by the job.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Shared implementation checklist

  • Define the output contract: paper size, orientation, margins, background colors, page numbering and whether text must be selectable.
  • Keep print styles separate from interactive controls. Hide buttons, menus, hover-only affordances and transient alerts.
  • Wait for data, images, web fonts and charts. Add an explicit readiness marker for automation.
  • Test long tables, intentional page breaks, repeating headers, orphaned headings and images near page boundaries.
  • Use deterministic locale, timezone and currency settings for server jobs.
  • Protect private routes and never allow unrestricted URL fetching from a public PDF endpoint.
  • Record the package, browser and operating-system versions used to generate archived documents.

Troubleshooting common failures

The PDF is blank or missing late content

The capture happened before React finished rendering or before data arrived. Add a readiness selector, wait for it, and verify that the URL is authenticated in the same browser context.

Styles look different from the page

PDF methods use print media. Add explicit @media print rules, enable background printing where supported, and check whether a style depends on an ancestor that is not part of the printed subtree.

Images disappear

Check CORS response headers, image authentication and lazy-loading behavior. For automation, wait for image completion; for canvas conversion, ensure cross-origin images are permitted.

Text cannot be selected

That is expected for a canvas-based html2pdf.js export in many cases. Use browser print or a headless-browser PDF when searchable text is a requirement.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Mobile users cannot print

Mobile WebViews and Firefox for Android have documented limitations in the react-to-print project. Offer a server-generated PDF or a download path for those environments and test the exact WebView wrapper.

Chromium fails to start in a container

Install the browser dependencies and fonts required by your automation package, check sandbox configuration, and capture the browser’s startup stderr. Do not “fix” this by disabling security globally without understanding the container isolation.

Jobs time out or exhaust memory

Set navigation and PDF timeouts, cap concurrency, reuse a browser process, close contexts promptly and split exceptionally large documents. Measure queue wait and browser memory in your own deployment; the available documentation does not establish a universal performance benchmark.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server when you need a clean image or PDF from a URL rather than maintaining your own browser automation. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and each response identifies the result with X-Page-Verdict and X-Billed headers.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

One GET request returns a PNG, JPEG, WebP or PDF. The API supports full-page captures with lazy images loaded, CSS-element capture, dark mode, device presets or custom viewports, retina scale, PDF paper and page options, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify migration. An MCP server provides take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients.

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 API documentation for response and option details. The Free plan includes 1,000 shots per 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.

FAQ

Can react-to-print download a PDF without opening print preview?

Not by itself. It invokes the browser print flow; use its custom print hook to connect the content to another generator if you need an automated file.

Does html2pdf.js work in a Node.js API route?

No. It depends on browser APIs, html2canvas and jsPDF. Use Puppeteer or Playwright for server-side browser generation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Which option preserves selectable text?

Browser print and headless-browser PDF generation are the approaches to evaluate first. Canvas-based output from html2pdf.js may be image-based and therefore not selectable or searchable.

Do Puppeteer and Playwright use screen styles for PDF output?

They generate with print CSS media. In Puppeteer, explicitly emulate screen media first when the intended design is the screen stylesheet rather than the print stylesheet.

Frequently Asked Questions

Can react-to-print download a PDF without opening print preview?

Not by itself. It invokes the browser print flow; use its custom print hook to connect the content to another generator if you need an automated file.

Does html2pdf.js work in a Node.js API route?

No. It depends on browser APIs, html2canvas and jsPDF. Use Puppeteer or Playwright for server-side browser generation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Which option preserves selectable text?

Browser print and headless-browser PDF generation are the approaches to evaluate first. Canvas-based output from html2pdf.js may be image-based and therefore not selectable or searchable.

Do Puppeteer and Playwright use screen styles for PDF output?

They generate with print CSS media. In Puppeteer, explicitly emulate screen media first when the intended design is the screen stylesheet rather than the print stylesheet.

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.