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

For most React apps, use react-pdf: it wraps PDF.js in React’s Document and Page components. Configure its PDF.js worker in the same module as those components, then render the pages you need. If you need lower-level control over canvas rendering, use pdfjs-dist directly. In either case, the worker must be available to the browser and match the installed PDF.js version.

Choose the right PDF.js layer for your React app

PDF.js has three layers: a core layer that parses and interprets PDF data, a display layer that exposes APIs for rendering and document information, and a viewer layer that provides a user interface built on the display layer. Most React integrations use the display layer, either directly through pdfjs-dist or through the React-PDF wrapper.

  • Choose React-PDF when you want React components for loading a document and displaying pages, with callbacks and React-oriented loading and error patterns.
  • Choose pdfjs-dist directly when you want to manage loading, page state, canvas sizing, and rendering yourself.
  • Build on the viewer layer only when its interface is a useful starting point. Mozilla describes the viewer as a UI built on the display layer and asks developers to reskin or build upon it rather than copy the embedded viewer unchanged.

The examples below use the browser-side display API. They assume the PDF can be fetched by the app; serving the file, handling cross-origin access, and authorizing access to it are separate concerns from drawing its pages.

Install and configure React-PDF

Check the package’s current requirements

Install React-PDF with:

npm install react-pdf

The React-PDF README describes its 11.x branch as requiring React 19 or later and Node.js 22.13.0 or later. It lists current major browsers with minimums of Chrome 125 and Safari 18, including iOS 18. These requirements can change, so check the README for the version you intend to install before upgrading an existing app or choosing a deployment target.

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

Set the worker in the component module

React-PDF uses PDF.js under the hood. The browser worker performs PDF processing separately from the UI thread, so the app must make a compatible worker file available. For a bundler setup that supports import.meta.url, configure it in the same module that imports and renders Document or Page:

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

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

Keep this assignment alongside the React-PDF imports and component. React-PDF warns that placing the assignment in a separate module can allow module execution order to overwrite the custom workerSrc. A missing or incompatible worker is a common cause of the “fake worker” or worker-loading errors people encounter when setting up PDF.js.

Render a page with React-PDF

This component tracks the total page count after the document loads and lets the user move between pages:

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="/somefile.pdf"
        onLoadSuccess={({ numPages }) => setNumPages(numPages)}
      >
        <Page pageNumber={pageNumber} />
      </Document>
      <p>Page {pageNumber} of {numPages ?? '…'}</p>
      <button
        type="button"
        disabled={pageNumber <= 1}
        onClick={() => setPageNumber(page => page - 1)}
      >
        Previous
      </button>
      <button
        type="button"
        disabled={!numPages || pageNumber >= numPages}
        onClick={() => setPageNumber(page => page + 1)}
      >
        Next
      </button>
    </section>
  );
}

Replace /somefile.pdf with the path or URL your app uses. The example renders one page at a time; it does not create a complete multi-page document viewer with navigation, zoom, or search. Add those controls if your use case needs them. React-PDF’s maintained example also demonstrates wrapping the document and page in Suspense and an Error Boundary; use those patterns when they fit your app’s loading and error-handling design.

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.

Configure workers for your build

The new URL(..., import.meta.url) pattern is a convenient bundler setup, but it is not the only documented option. Choose a worker delivery method that fits your build and keep the worker version matched to the pdfjs-dist version used by React-PDF.

  • Bundle it: use the new URL pattern shown above when your bundler supports it.
  • Copy it into the output: React-PDF documents copying pdf.worker.mjs into the build output and pointing workerSrc at the resulting URL.
  • Use a version-matched CDN: React-PDF documents a URL pattern based on pdfjs.version: //unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs. This avoids hard-coding a separate version, but the browser must be able to reach the CDN.
  • Support older browsers: React-PDF documents replacing /build/ with /legacy/build/ for the legacy worker. That worker alone does not guarantee compatibility: polyfills and bundler transpilation may also be necessary.

For direct pdfjs-dist use with Webpack, Mozilla’s setup guidance says to bundle the worker separately; its pdfjs-dist/webpack entry can provide autoconfiguration. For other bundlers, follow that bundler’s handling of worker assets and verify that the emitted worker URL is reachable in the deployed app, not only in development.

Render with pdfjs-dist directly

Use the display API directly when the React-PDF component model does not give you the control you need. The minimal lifecycle is: configure the worker, load the document, request a page, calculate its viewport, size a canvas, render, and await the render task.

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 context = canvas.getContext('2d');
  if (!context) {
    throw new Error('Could not create a 2D canvas context');
  }

  const loadingTask = pdfjsLib.getDocument(pdfPath);
  const pdfDocument = await loadingTask.promise;
  const pdfPage = await pdfDocument.getPage(pageNumber);
  const viewport = pdfPage.getViewport({ scale: 1.0 });

  canvas.width = viewport.width;
  canvas.height = viewport.height;

  const renderTask = pdfPage.render({
    canvasContext: context,
    viewport,
  });
  await renderTask.promise;

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

Call it after the canvas exists in the DOM, for example from a React effect. If a component can unmount or start another render while loading, add cleanup and cancellation handling appropriate to your app so an old task does not race a newer one. The example uses scale 1.0 for a straightforward viewport; choose a different scale if you need a larger or smaller rendered page and size the canvas from that viewport.

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

Package assets for text, annotations, and special PDFs

A page can render while optional layers or characters are missing if supporting assets or styles are absent. Add only the assets your PDFs and interface require.

  • Links and annotations: import react-pdf/dist/Page/AnnotationLayer.css when annotation elements such as links should display correctly.
  • Selectable text: import react-pdf/dist/Page/TextLayer.css when using the text layer. A canvas image alone is not selectable text.
  • Non-Latin characters: copy pdfjs-dist/cmaps into a served location or serve them from a CDN, then provide a stable options object such as { cMapUrl: '/cmaps/' } to Document.
  • JPEG 2000 content: PDFs that use JPEG 2000 may require the wasm directory and a wasmUrl option.
  • Standard fonts: PDFs that rely on standard fonts may require the standard_fonts directory and a standardFontDataUrl option.

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

Run the app over HTTP

Do not open the React app as a local file:// page. Mozilla’s guidance is explicit: the PDF.js worker is not enabled for file:// URLs, so use a server. Run the app through its development server while building, and serve the built app over HTTP in deployment. This also gives the worker and PDF files browser-accessible URLs rather than local file paths.

Troubleshoot common React PDF.js errors

Worker failed to load, or PDF.js fell back to a fake worker

  • Likely cause: workerSrc is unset, points to a URL that is not emitted or served, or uses a worker version that differs from the installed PDF.js package.
  • Fix: configure the worker beside the React-PDF imports; check the browser Network panel for the worker request; verify the deployed URL and ensure the worker comes from the same pdfjs-dist version.

The app was opened from a file path

  • Likely cause: the page uses a file:// URL, where the worker is not enabled.
  • Fix: start an HTTP development server and access the app through its served URL.

Text or links are missing even though the page image appears

  • Likely cause: the text or annotation layer styles are missing, or the relevant layer is not enabled.
  • Fix: import TextLayer.css for selectable text and AnnotationLayer.css for annotations such as links, then verify the layer configuration in the component.

Some characters appear as boxes or do not render

  • Likely cause: required character maps or standard font data are unavailable to PDF.js.
  • Fix: serve the cMaps or standard font assets and configure their URLs through the relevant options. Keep the options object stable between React renders.

A PDF with JPEG 2000 images fails or renders incompletely

  • Likely cause: the required PDF.js WASM assets are not included in the deployed output.
  • Fix: package and serve the wasm directory, then set wasmUrl as required by the React-PDF setup.

The canvas is blank or too small

  • Likely cause: rendering was not awaited, the canvas has not mounted, or its dimensions do not match the page viewport.
  • Fix: in direct rendering, get the page viewport, set the canvas width and height from it, and await renderTask.promise. In React-PDF, confirm that the document loaded and that the requested page number is valid.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is to capture a URL as an image or PDF rather than build an interactive PDF reader into your React app, ScreenshotNeo is a website screenshot API and MCP server. It is not a replacement for PDF.js page rendering inside React; it captures a web URL. A one-call image example is:

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

See the ScreenshotNeo API documentation for request options. It removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The Free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Sign up for the free plan.

Performance and reliability considerations

Both approaches depend on loading a PDF, processing the requested page, and delivering a matching worker and any required auxiliary assets. React-PDF handles the component integration; direct pdfjs-dist leaves more lifecycle and canvas work to your application. For a multi-page document, rendering only the pages currently needed avoids asking the browser to draw every page at once. For either approach, verify the production build’s worker and asset URLs and test representative PDFs, especially those that use non-Latin text, annotations, JPEG 2000, or standard fonts.

Do not treat a successful local development render as proof that production is configured: a worker or asset that the dev server resolves may be missing from the deployed output. Check failed network requests and the browser console when diagnosing a deployment-only problem.

FAQ

Can I display a PDF without React-PDF?

Yes. Install pdfjs-dist and use its display API to load the document and render pages to a canvas. You will manage the loading, canvas, page state, worker, and errors yourself.

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

Does PDF.js provide a complete reader interface?

The viewer layer supplies a UI, but the React-PDF examples here focus on rendering pages. A production reader may also need its own navigation, zoom, loading and error UI, and accessibility decisions.

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.