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.

Use a React ref to target the rendered component, then pass that DOM node to jsPDF’s html() method from a user action. jsPDF renders the element through html2canvas, so the result is a browser-side reconstruction of your HTML and CSS rather than a literal screenshot. When rendering finishes, save the document in the callback.

This approach is practical when you already have a styled React view and want a download button. It is not print-perfect for every CSS rule, image origin, font, or very long layout. The complete setup below shows the reliable baseline, then covers fidelity limits, security, alternatives, and failure recovery.

Install the packages

Install jsPDF in the React project. The html() path also depends on html2canvas, so install it explicitly when your package manager does not bring it in with your chosen jsPDF build.

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.
npm install jspdf html2canvas

Confirm the import syntax against the exact jsPDF version in your lockfile. The current documentation demonstrates a named jsPDF import, but bundler interop can differ between releases and build tools.

Build the basic React export

The ref must point to the element that is actually rendered. Put only document content inside that element; keep navigation, buttons, loading indicators, and other transient controls outside it.

import { useRef } from 'react';
import { jsPDF } from 'jspdf';

function Report() {
  const reportRef = useRef(null);

  const downloadPdf = () => {
    const element = reportRef.current;
    if (!element) return;

    const doc = new jsPDF({
      orientation: 'portrait',
      unit: 'mm',
      format: 'a4',
    });

    doc.html(element, {
      callback: (pdf) => pdf.save('report.pdf'),
      margin: [10, 10, 10, 10],
      autoPaging: 'text',
    });
  };

  return (
    <>
      <section ref={reportRef}>
        <h1>Report</h1>
        <p>Content to export</p>
      </section>
      <button type="button" onClick={downloadPdf}>
        Download PDF
      </button>
    </>
  );
}

export default Report;

The click handler runs after React has mounted the section, so reportRef.current is a DOM node rather than null. The callback is important: saving there waits for the asynchronous HTML rendering to finish. The example requests A4 portrait pages in millimetres, 10 mm margins, and text-aware automatic paging.

Keep the export surface deliberate

Render an export-only wrapper when the on-screen component contains controls or responsive chrome. You can duplicate the data into a print layout, hide elements with export-specific CSS, or conditionally omit them before calling html(). A smaller, purpose-built DOM tree usually produces more predictable pagination than capturing an entire application shell.

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

Choose page geometry and rendering options

jsPDF creates the document before it renders the node. Set these values intentionally rather than relying on defaults:

Setting What it controls Practical choice
orientation Page direction 'portrait' for reports; 'landscape' for wide tables
unit Coordinates and margins 'mm' is convenient for paper measurements
format Paper size 'a4', or another format supported by your installed release
margin Whitespace around rendered content Use an array such as [10, 10, 10, 10] and test the target browser
autoPaging How HTML content is split across pages 'text' can reduce awkward text cuts; verify complex layouts

For exact page composition, jsPDF’s drawing API is more deterministic than converting arbitrary DOM. You can create pages, place text and graphics at known coordinates, and call save() yourself. That is more work, but it avoids depending on browser layout reconstruction.

What jsPDF’s HTML path really does

The html() method uses html2canvas. html2canvas rebuilds a visual representation from DOM information; it does not take a literal screenshot of the browser surface. CSS that html2canvas does not support, pseudo-elements with unusual behavior, filters, some transforms, and complex responsive interactions may therefore appear differently in the PDF. The html2canvas documentation lists its support and limitation details at https://html2canvas.github.io/html2canvas/documentation/.

Inspect the generated file in every browser you support. If a particular card, chart, or table is important, simplify its CSS for export or create an export-only variant. There are no universal fidelity or speed guarantees for arbitrary React layouts, and the available project documentation does not provide a cross-browser performance benchmark.

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

Images, fonts, and cross-origin resources

Remote images and other resources must be available to the browser under normal web security rules. Cross-origin images can be skipped or taint the canvas unless the server supplies suitable CORS permissions. Configure the asset server for the required origin, serve assets from the same origin, or use a controlled proxy where appropriate. html2canvas cannot bypass content-security or cross-origin restrictions; its getting-started guidance explains the browser prerequisites at https://html2canvas.github.io/html2canvas/getting-started/.

Fonts deserve the same check. jsPDF’s standard 14 fonts have limited ASCII coverage. If your report contains accented characters, non-Latin scripts, or symbols outside that range, embed a custom TTF containing the required glyphs and verify the result in the generated PDF. Do not assume that a font loaded by CSS will automatically become an embedded PDF font.

Long documents and page breaks

Long, nested layouts are where DOM-to-PDF conversion needs the most testing. Break content into logical blocks, avoid huge fixed-height containers, and check headings that land at the bottom of a page. Use an export stylesheet to reduce oversized margins and animations. The autoPaging option helps, but it is not a guarantee that every flex, grid, positioned element, or table will break as a human would expect.

Run conversion only after the content is ready

Do not invoke conversion while data is still loading. Disable the button until the report query, images, and fonts needed by the export have finished. If a component changes immediately before the click, let React commit that update first (for example, by enabling the button only after the ready state is true). A ref identifies the current node; it does not wait for asynchronous application data.

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

For interactive widgets, capture a stable state. Close menus, stop animations, and replace canvas-based charts with a deterministic export representation when necessary. A loading spinner or blinking cursor can otherwise become part of the PDF.

Security and text handling

Sanitize untrusted content before rendering it into the component and before passing it to jsPDF. The jsPDF documentation states: “We strongly advise you to sanitize user input before passing it to jsPDF!” Treat report data, HTML fragments, URLs, and filenames as untrusted. Avoid injecting raw markup merely to make export work, and validate any user-controlled values used in custom drawing code.

Sanitization does not solve cross-origin problems or guarantee safe CSS. Keep the export component’s styles and resource URLs under your control, and test with the same content classes your users can submit.

Browser-only limitation

This html2canvas-based workflow runs in a browser because it needs a DOM and browser rendering APIs. It is not suitable for a Node.js-only process or a server function with no browser environment. If your requirement is server-side generation, use a PDF-native renderer or a browser-capable service rather than calling this DOM path in plain Node.js.

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

Alternatives when DOM reuse is not the priority

React PDF

React PDF uses PDF-specific React components such as Document, Page, and Text. Its web documentation also provides PDFDownloadLink. You describe the PDF layout directly instead of asking html2canvas to interpret an existing DOM tree. This is a better fit when pagination and typography must be designed as a document, but it requires a separate component tree and styling model. See the v2 component documentation at https://react-pdf.org/docs/v2/components.

html2pdf.js

html2pdf.js packages a client-side element-to-PDF workflow around html2canvas and jsPDF. Its README states that it must run in a browser: https://github.com/eKoopmans/html2pdf.js/. It can be convenient when you prefer its chaining API, but it inherits the same fundamental DOM reconstruction and cross-origin limitations.

Requirement DOM capture with jsPDF PDF-native React components
Reuse existing HTML and CSS Strong fit Requires a separate document tree
Exact pagination and coordinates Needs testing and export-specific CSS More explicit control
Browser-only download Yes Web download components are available
Server-only Node execution Not suitable Choose an implementation that supports your deployment target
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting

Symptom Likely cause Fix
Nothing downloads The ref is null or saving occurs before rendering completes Call from a user action after mount and place pdf.save() inside the callback.
Images are missing Cross-origin response lacks suitable CORS headers, or the image is not loaded yet Serve the asset with appropriate CORS, use same-origin hosting or a controlled proxy, and wait until images are ready.
Text or CSS looks different html2canvas does not support the CSS feature exactly as the browser does Simplify the export CSS, create an export-only layout, or switch to PDF-native components.
Characters become boxes or disappear The selected PDF font lacks required glyphs Embed a custom TTF with coverage for the report’s scripts and test the actual output.
Pages split awkwardly Complex flex/grid, fixed heights, or oversized blocks Remove rigid heights, break content into smaller sections, adjust margins, and test autoPaging in target browsers.
Server rendering throws DOM errors The html2canvas path is being run without a browser Move conversion to a browser interaction or adopt a renderer designed for the server environment.
Export includes buttons or menus The ref wraps application chrome Move the ref to a document-only wrapper or render a dedicated export component.

Test checklist before shipping

  • Open the export in each supported browser and inspect every page.
  • Test short, very long, and empty datasets.
  • Test remote images, custom fonts, accented text, and non-Latin scripts.
  • Check landscape output for wide tables and verify margins on paper-sized pages.
  • Capture the component only after data, images, and fonts are ready.
  • Try user-controlled text and URLs to confirm sanitization and safe resource handling.
  • Decide whether a dedicated PDF-native layout is preferable for a designed document.

Or skip the browser setup

If the React report is available at a public or authenticated URL, ScreenshotNeo can capture the rendered page without you maintaining browser automation. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers.

For an HTTP request, the documented call is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://your-site.example/report -o report.webp

See the ScreenshotNeo documentation for the current request and PDF options. The same service offers an MCP server for Claude, Cursor, and other MCP clients, with take_screenshot, get_page_info, and capture_pdf tools. PDF captures can specify paper size, margins, landscape mode, and page ranges.

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

Equivalent client calls are:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://your-site.example/report"}, timeout=90)
r.raise_for_status()
open("report.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://your-site.example/report' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`ScreenshotNeo request failed: ${res.status}`);

It also supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, retina scale, custom CSS and JavaScript, clicks before capture, selector waits or delays, network-idle waits, ad/tracker/request blocking, custom headers, cookies, user agents and Authorization, timezone and geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, an OpenAPI specification, and familiar parameter names for easier migration.

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing gives two months free, and every feature is included on every plan. Create a free ScreenshotNeo account to try it.

Frequently Asked Questions

Can I export only one nested element instead of the whole component?

Yes. Attach the React ref directly to the nested section you want to convert, and keep unrelated controls outside that element.

Does jsPDF automatically preserve my site’s web fonts?

No. Verify glyph coverage and embed a suitable custom TTF when the report contains characters outside the limited ASCII range of the standard PDF fonts.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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.