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.

For most React apps, use react-pdf: it wraps PDF.js in Document and Page components. Set PDF.js’s worker in the same module that imports those components, keep the worker version matched to pdfjs-dist, and serve the app over HTTP. Choose the lower-level pdfjs-dist display API instead when you need to manage the canvas and rendering lifecycle yourself.

Choose a React integration

PDF.js is Mozilla’s JavaScript PDF toolkit, organized into three layers: a core layer that parses and interprets PDF data, a display layer for rendering pages and retrieving document information, and a viewer layer that provides a user interface built on the display layer. Most React applications use the display layer through a React wrapper rather than embedding the full viewer. The viewer can serve as a starting point for a custom interface; Mozilla advises against copying its embedded viewer unchanged.

Approach What you manage Best fit
react-pdf React components, callbacks, page state, and PDF.js configuration A React-first viewer where you want document and page components
pdfjs-dist display API Worker and bundler setup, loading tasks, canvas sizing, rendering, and application state A custom canvas workflow or finer control over rendering

Both routes rely on PDF.js and its worker. The wrapper reduces the amount of rendering lifecycle code you write; it does not remove the need to configure and ship compatible PDF.js assets.

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

Check the package and runtime requirements

Install the React wrapper with npm install react-pdf. Its current README describes the 11.x branch as requiring React 19 or later and Node.js 22.13.0 or later. It lists minimum browser versions of Chrome 125 and Safari 18, including iOS 18 for Safari. These package and browser requirements can change between releases, so check the README for the version you intend to install before upgrading or choosing a deployment target.

For a lower-level integration, install pdfjs-dist with npm install pdfjs-dist --save. Mozilla’s setup guidance notes that Webpack projects must bundle the worker separately; its pdfjs-dist/webpack entry can provide automatic configuration.

Render a PDF with React-PDF

Configure the worker beside the React-PDF imports

Set workerSrc in the same module that imports and renders Document or Page. React-PDF warns that assigning the worker path in a separate module can fail: module execution order may later overwrite the custom value.

import { pdfjs, Document, Page } from 'react-pdf';

pdfjs.GlobalWorkerOptions.workerSrc = new URL(
  'pdfjs-dist/build/pdf.worker.min.mjs',
  import.meta.url,
).toString();

This URL-based setup lets a compatible bundler resolve the worker asset from the installed pdfjs-dist package. If your bundler cannot process that URL, use one of the documented alternatives: copy pdf.worker.mjs into the output directory and point workerSrc there, or use a CDN URL tied to the installed PDF.js version. For example, React-PDF documents this version-matched pattern:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
pdfjs.GlobalWorkerOptions.workerSrc =
  `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;

Do not select an arbitrary worker version. The worker must match the PDF.js package version; a mismatch can prevent documents from loading. For older browsers, React-PDF documents replacing /build/ with /legacy/build/. The legacy worker by itself does not guarantee full backward compatibility: polyfills and bundler transpilation may also be required.

Add a document and page

This component loads a PDF from a URL, records the page count when loading succeeds, and lets a reader move between pages. The file URL must be reachable by the browser; cross-origin PDF hosting must also permit the request under its CORS policy.

import { useState } from 'react';
import { pdfjs, Document, Page } from 'react-pdf';

pdfjs.GlobalWorkerOptions.workerSrc = new URL(
  'pdfjs-dist/build/pdf.worker.min.mjs',
  import.meta.url,
).toString();

export default function PdfViewer() {
  const [numPages, setNumPages] = useState<number>();
  const [pageNumber, setPageNumber] = useState(1);

  return (
    <section>
      <Document
        file="/documents/guide.pdf"
        onLoadSuccess={({ numPages }) => {
          setNumPages(numPages);
          setPageNumber(1);
        }}
      >
        <Page pageNumber={pageNumber} />
      </Document>
      <div>
        <button
          type="button"
          disabled={pageNumber <= 1}
          onClick={() => setPageNumber((page) => page - 1)}
        >
          Previous
        </button>
        <span>Page {pageNumber} of {numPages ?? '…'}</span>
        <button
          type="button"
          disabled={numPages === undefined || pageNumber >= numPages}
          onClick={() => setPageNumber((page) => page + 1)}
        >
          Next
        </button>
      </div>
    </section>
  );
}

When you add loading and error states, React-PDF’s maintained example uses Suspense and an Error Boundary around the document and page. A failed load should be visible to the user rather than leaving a blank viewer. For a production interface, also disable navigation until the document has loaded and handle the case where a newly selected file has fewer pages than the previous one.

Render directly with pdfjs-dist

The display API gives you the loading and rendering lifecycle explicitly. Configure the worker, load the PDF, request a page, calculate a viewport, size the canvas, start rendering, and await the render task. In a real React component, obtain the canvas from a ref after it mounts; do not query the DOM before the canvas exists.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import * as pdfjsLib from 'pdfjs-dist';

pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
  'pdfjs-dist/build/pdf.worker.min.mjs',
  import.meta.url,
).toString();

export async function renderPdfPage(pdfPath, canvas, pageNumber = 1) {
  const loadingTask = pdfjsLib.getDocument(pdfPath);
  const pdfDocument = await loadingTask.promise;
  const pdfPage = await pdfDocument.getPage(pageNumber);
  const viewport = pdfPage.getViewport({ scale: 1.0 });
  const context = canvas.getContext('2d');

  if (!context) {
    throw new Error('Could not create a 2D canvas context.');
  }

  canvas.width = viewport.width;
  canvas.height = viewport.height;
  await pdfPage.render({ canvasContext: context, viewport }).promise;

  return { numPages: pdfDocument.numPages, pageNumber };
}

The scale value controls the viewport’s rendered dimensions: increasing it creates a larger canvas and uses more memory. For sharper output on high-density screens, size the backing canvas for the desired pixel density while keeping its CSS display dimensions at the intended layout size. For React, also prevent stale asynchronous renders from overwriting a newer page or document; cancel an in-progress render task when the component changes or unmounts, and ignore results from superseded loading tasks.

Package supporting assets when your PDFs need them

Some PDFs depend on assets beyond the worker. Missing assets may affect only particular document features or character sets, so a simple Latin-language sample is not enough to validate every production PDF.

  • Links and annotations: import react-pdf/dist/Page/AnnotationLayer.css when annotation-layer features such as links need styling.
  • Select-and-copy text: import react-pdf/dist/Page/TextLayer.css when using the text layer. The canvas alone displays page content but does not provide the same selectable text layer.
  • Non-Latin characters: package or serve PDF.js character maps (cMaps), then provide a stable option such as { cMapUrl: '/cmaps/' } to Document. The path must resolve to the deployed cMap assets.
  • JPEG 2000 content: some PDFs require the PDF.js wasm directory and a wasmUrl option.
  • Standard fonts: PDFs using standard fonts may require the standard_fonts directory and a standardFontDataUrl option.

Keep the options object outside the component or memoize it. Creating a new object on every render can make React-PDF treat options as changed repeatedly and trigger unnecessary work.

Serve the React app over HTTP

Do not test the viewer by opening the app’s HTML file directly with a file:// URL. Mozilla’s guidance is explicit: the PDF.js worker is not enabled for file:// URLs, so use a server. Run your normal development server or serve the built app over HTTP, then load the PDF through that app. This also gives you a realistic environment for checking asset paths, worker delivery, and cross-origin access.

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

Troubleshoot common PDF.js React failures

“No GlobalWorkerOptions.workerSrc specified” or a worker load error

  • Confirm the workerSrc assignment runs in the same module as the React-PDF imports and before rendering the document.
  • Check the browser network panel for the worker request. A 404 usually means the bundled or copied worker path does not match the deployed output path.
  • If using a CDN, make its version match pdfjs.version; do not hard-code a different release.
  • Verify the app is served over HTTP, not opened as a local file.

“API version does not match Worker version”

The display package and worker are from different versions. Remove stale copied worker files, rebuild, and ensure that the worker URL resolves to the version installed with pdfjs-dist. A versioned CDN path can help keep the pairing explicit.

The PDF loads but annotations or selectable text look wrong

Check that the annotation and text layer stylesheets are imported for the features you use. Those layers are separate from the page canvas; a correctly rendered canvas does not prove that their styles or content are configured.

Some characters are missing or a particular PDF fails

Inspect the browser console and network panel for failed cMap, WASM, or standard-font asset requests. Package the required directory and set the corresponding URL option. Test with PDFs that contain the scripts, fonts, and image formats your users actually submit.

A remote PDF does not load

Confirm that the URL returns a PDF to the browser and that its host allows cross-origin access from your app’s origin. If the URL requires private credentials or expires, serve the document through an application-controlled endpoint instead of exposing credentials in client-side code.

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

Repeated rendering, flicker, or stale pages

Keep options stable, avoid starting duplicate loading tasks on every render, and cancel or disregard work when the selected document or page changes. In the direct API route, await the render task and manage its cancellation during component cleanup.

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

Performance, reliability, and cost decisions

Rendering PDF pages means allocating canvas memory and doing work in the browser. Rendering only the visible page is generally more manageable than drawing every page at once for a long document. Avoid retaining oversized canvases after a document changes, and choose scale based on display quality rather than defaulting to the largest possible bitmap. For large files or slow networks, present loading and failure states so users know whether the document is still loading or could not be reached.

Package and deploy the worker and any auxiliary assets as part of the application build, or use a version-matched CDN deliberately. A CDN can simplify delivery but makes successful rendering dependent on that remote asset being reachable and permitted by your site’s policies. A locally bundled worker gives your app control of deployment, but requires checking the emitted asset path whenever bundler configuration changes. PDF.js and React-PDF are software components; the setup described here does not require a paid screenshot or rendering API.

Or skip the browser setup

ScreenshotNeo is a website screenshot API, not a replacement for rendering a PDF inside your React interface. Use it when the task is to capture a webpage as an image or PDF rather than build an in-app PDF reader. One GET request returns a PNG, JPEG, WebP, or PDF; see the ScreenshotNeo API documentation.

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.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; those steps can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses identify the page verdict and billing status in headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month without a card.

Frequently Asked Questions

Can I open a local PDF from a file input?

Yes. Read the selected file in the browser and provide its data to the PDF component; do not open the app itself through a file:// URL, because the PDF.js worker is not enabled there.

Does the canvas make PDF text selectable?

Not by itself. Use the text layer and its stylesheet if users need selectable text.

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

Can I use PDF.js without React-PDF?

Yes. Install pdfjs-dist and use its display API to load a document, fetch pages, create viewports, and render them to a canvas.

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.