DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
PDF

How to Convert a Webpage to PDF in React

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

The right React-to-PDF method depends on who starts the export. For a person clicking an Export button, use the browser print flow—window.print() with print-specific CSS, optionally narrowed to one component with react-to-print. For automatic, repeatable files generated by your application, render the page in a headless browser and call Puppeteer’s Page.pdf(). The first route hands saving to the user’s print dialog; the second returns PDF bytes your server can store or deliver.

Choose the workflow before writing code

Question Browser print flow Headless browser generation
Who initiates? The user clicks a button and operates the print dialog. Your application navigates to a page and creates the file.
Where does saving happen? The browser’s print UI; the user chooses a PDF destination. Your code receives PDF output for download, storage, email, or another workflow.
Which styles apply? @media print and @page rules. Puppeteer uses print media by default; you can emulate screen media first.
What can be selected? window.print() prints the document; react-to-print can print a referenced component. Usually a route or template that renders exactly the content to export.
Operational requirement Only the end user’s browser. A server-side or hosted browser-rendering environment.

There is no documented performance, cost, or fidelity winner in the cited APIs. Select based on interaction, output ownership, layout control, and whether you can operate a browser process.

Option 1: let the user save a React page as PDF

Build a print-safe component

Keep the printable content in a semantic container and mark controls that should disappear on paper. A normal React component might look like this:

export default function Invoice({ invoice }) {
  return (
    <main className="invoice">
      <header className="invoice__header">
        <h1>Invoice {invoice.number}</h1>
        <button className="no-print" onClick={() => window.print()}>
          Save as PDF
        </button>
      </header>
      <section aria-label="Invoice details">
        <p>Issued: {invoice.issuedAt}</p>
        {/* line items and totals */}
      </section>
    </main>
  );
}

window.print() opens the print dialog for the current document. It does not silently write a PDF file or return PDF bytes. The available destinations and controls depend on the browser and operating system.

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

Add print media CSS

/* app.css */
.invoice {
  max-width: 900px;
  margin: 2rem auto;
  color: #202124;
  background: white;
}

@media print {
  .no-print,
  nav,
  .chat-widget,
  .newsletter {
    display: none !important;
  }

  .invoice {
    max-width: none;
    margin: 0;
    font-size: 11pt;
  }

  /* Avoid splitting a row or card across pages where possible. */
  .invoice__line-item,
  .invoice__summary {
    break-inside: avoid;
  }

  a {
    color: inherit;
    text-decoration: none;
  }
}

@page {
  size: A4 portrait;
  margin: 16mm;
}

Use @media print to remove navigation, buttons, overlays, and other interactive chrome; adjust widths and typography for paper; and control page breaks. The @page rule sets paper dimensions, orientation, and margins. Test long tables and cards because a break-avoid rule is a request to the browser, not an absolute guarantee.

Prepare state around printing when necessary

Some components need a temporary change—such as expanding a collapsed section or replacing a canvas preview with text. Browsers expose beforeprint and afterprint events for that purpose:

import { useEffect, useState } from 'react';

export function PrintableDashboard() {
  const [printMode, setPrintMode] = useState(false);

  useEffect(() => {
    const before = () => setPrintMode(true);
    const after = () => setPrintMode(false);
    window.addEventListener('beforeprint', before);
    window.addEventListener('afterprint', after);
    return () => {
      window.removeEventListener('beforeprint', before);
      window.removeEventListener('afterprint', after);
    };
  }, []);

  return <Dashboard showPrintDetails={printMode} />;
}

Do not treat these events as a file-generation callback. The user still controls the print preview and destination.

Print one component with react-to-print

When the application document contains unrelated navigation or multiple widgets, react-to-print can target a component through a ref and invoke the browser print flow. The essential pattern is:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { useRef } from 'react';
import { useReactToPrint } from 'react-to-print';

export function ReportPage() {
  const reportRef = useRef(null);

  const printReport = useReactToPrint({
    contentRef: reportRef,
    documentTitle: 'sales-report',
    onBeforePrint: async () => {
      // Resolve data or fonts that must exist before preview opens.
    },
  });

  return (
    <>
      <button onClick={printReport}>Print report</button>
      <section ref={reportRef} className="report">
        <h1>Sales report</h1>
        {/* printable report */}
      </section>
    </>
  );
}

Check the installed package documentation for the exact option names used by your version; APIs can change. The package documentation explicitly says the native route cannot directly download a PDF through print preview. If you need a file without that preview, use a separate PDF generator or the server-side approach below. A custom print callback can hand the rendered content to such a generator, but that generator’s DOM, pagination, and font behavior must be evaluated independently.

Option 2: generate the PDF automatically with Puppeteer

Install and render a route

Puppeteer’s documented flow is to launch a browser, create a page, navigate to the React route, call page.pdf(), and close the browser. The following Node.js example writes the resulting bytes to disk:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({
  // Add your deployment's approved Chromium launch options here.
});

try {
  const page = await browser.newPage();
  await page.goto('https://example.com/reports/123?print=1', {
    waitUntil: 'networkidle0',
  });

  // Optional: use screen rules instead of print rules.
  // await page.emulateMediaType('screen');

  await page.pdf({
    path: 'report-123.pdf',
    format: 'A4',
    printBackground: true,
    margin: {
      top: '16mm',
      right: '16mm',
      bottom: '16mm',
      left: '16mm',
    },
  });
} finally {
  await browser.close();
}

Page.pdf() generates a PDF using the print CSS media type by default. Call page.emulateMediaType('screen') before it when the screen stylesheet—not the print stylesheet—should control the output. PDF colors are normally adjusted for printing; use the CSS property -webkit-print-color-adjust when exact color rendering is required and your target browser supports it. Puppeteer’s guide states that fonts are awaited by default.

Make the route export-friendly

  • Provide a dedicated route or query flag that renders only the report, without navigation and transient controls.
  • Authenticate the page in a controlled way, using a short-lived session or request headers rather than exposing private data in a public URL.
  • Wait for the data and images your page needs. networkidle0 helps with network completion but does not fix an application that never settles or lazy-loads content only after scrolling.
  • Use print CSS deliberately. Puppeteer does not magically repair overflow, missing fonts, or components whose content exists only after a user gesture.

Control paper layout and color

Set format or explicit width and height, margins, landscape mode, background printing, and page ranges according to your document. Keep the same choices in CSS @page where possible so browser preview and automated output agree. If the design is intended for a screen-like capture, emulate screen and verify that its responsive breakpoint matches the viewport used by the page.

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

React rendering details that commonly affect PDFs

Fonts

Use a font loading strategy that completes before capture. Puppeteer’s PDF method waits for fonts by default, but a font URL that is blocked, unauthorized, or incorrectly declared will still produce a fallback. Host the font where the rendering browser can reach it and inspect the page for failed requests.

Images and lazy content

Native lazy loading, intersection observers, and infinite lists may leave content outside the initial viewport absent from the PDF. Prefer an export route that renders the complete dataset, or explicitly trigger loading before calling page.pdf(). For very large reports, paginate in the application rather than creating an unbounded DOM.

Canvas, video, and interactive widgets

Canvas output can depend on timing and device scale; video may show a poster or a blank frame. Replace interactive charts with a deterministic print representation when accuracy matters. Hide chat launchers, cookie notices, and menus in print CSS rather than hoping the PDF engine ignores them.

Screen versus print colors

Print media often changes backgrounds and colors to conserve ink. If brand colors are part of the document’s meaning, define print colors explicitly and decide whether printBackground belongs in the automated options. Exact visual equivalence between a browser preview and every PDF viewer is not guaranteed.

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

Deployment, reliability, and cost considerations

The browser route consumes the user’s local resources and inherits that environment’s fonts, permissions, print settings, and browser behavior. Server rendering makes the output application-managed and repeatable, but your service must provision a compatible browser, handle concurrent jobs, secure private routes, and close browser/page resources on every success and failure. The cited documentation does not provide controlled benchmarks for speed, memory, fidelity, or cost, so choose capacity from your own documents and traffic rather than a universal number.

For predictable jobs, isolate a print route, set explicit timeouts around navigation and data loading, log the URL and document identifier (not secrets), and return a useful error when a required asset fails. Consider a queue for expensive reports and retain the generated bytes only as long as your privacy policy allows. Playwright also exposes PDF options such as output path and scale, but its exact API is version-sensitive; consult the documentation for the version installed in your deployment before adapting the code.

Troubleshooting checklist

The button opens preview but the PDF is empty

Confirm that the printable component is mounted, is not hidden by a parent’s display:none, and has data before window.print() runs. With a component helper, verify the ref points to the actual DOM node and that print CSS does not hide it.

Navigation and buttons appear

Add a stable class such as no-print and hide it inside @media print. For a whole application print, hide global chrome; for a component print, ensure the helper copies the styles the component needs.

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.

Puppeteer captures a loading screen

Wait for the data request or a specific ready condition, not only the first navigation event. A route that renders a loading shell and fetches data afterward needs an application-level signal before page.pdf().

The PDF uses screen styling unexpectedly

Remember that Puppeteer uses print media by default. Remove an unnecessary emulateMediaType('screen') call, or add the print rules you intended.

Fonts or images are missing

Check browser-request logs, cross-origin permissions, authentication, and URLs reachable from the rendering environment. Wait for required assets and avoid relying on a user scroll to trigger lazy loading.

Pages break in the wrong places

Use semantic wrappers and break-inside: avoid, break-before, or break-after where appropriate. Reduce oversized fixed-height containers and test documents with unusually long names, tables, and localized text.

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

The server runs out of resources

Always close pages and browsers in finally blocks, limit concurrent jobs, and measure your own document mix. Reusing a controlled browser can reduce launch overhead, but it requires careful isolation between requests.

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 hosted website screenshot API and MCP server. It can return PNG, JPEG, WebP, or PDF from one request, including full-page captures, custom CSS and JavaScript, waits, device presets, and PDF paper settings. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

For a PDF of a public React route, the direct call is:

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, output selection, authentication, and the other capture options. The same API accepts the parameter names used by other screenshot APIs, which can simplify migration. It also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

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

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`ScreenshotNeo HTTP ${res.status}`);
const file = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', file));

ScreenshotNeo includes 1,000 shots per month free with no card. Paid plans start at $5 for 3,000 shots; every feature is on every plan, and yearly billing gives two months free. It is useful when you want cookie banners, popups, and chat widgets removed before the shot; failed loads and bot checks not to consume credits; or an MCP server for AI-driven capture, without operating Chromium yourself. Create a free ScreenshotNeo account to use the 1,000 monthly shots.

A practical decision checklist

  • Choose browser print when a person should inspect options and save through the familiar print dialog.
  • Choose react-to-print when that person should print one referenced component rather than the whole document.
  • Choose Puppeteer when your backend must produce and deliver a file without user interaction.
  • Use a hosted renderer such as ScreenshotNeo when you want an API or MCP workflow without maintaining browser infrastructure.
  • In every route, make data, fonts, images, page breaks, privacy, and failure handling explicit.

Frequently Asked Questions

Can React itself create a PDF file with window.print()?

No. It opens the browser’s print dialog. The user must select a PDF destination, or your application must use a separate generator such as a headless browser.

Should I use Playwright instead of Puppeteer?

Either can expose PDF options, but exact Playwright APIs are version-sensitive. Use the documentation matching the version installed in your deployment and validate the resulting layout.

Can I silently download a PDF with react-to-print?

The package’s documented native flow uses print preview and does not directly download a PDF. Use a dedicated PDF generator for an application-managed file.

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

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.