---
title: "react-pdf setup: Document and page rendering"
canonical_url: "https://www.nutrient.io/blog/react-pdf-setup-basic-rendering/"
md_url: "https://www.nutrient.io/blog/react-pdf-setup-basic-rendering.md"
last_updated: "2026-08-17T09:34:40.789Z"
description: "Install and configure react-pdf for rendering PDFs in React — worker setup, page sizing, rotation, dark mode, and the memoization gotcha."
---

**TL;DR**

`react-pdf` wraps PDF.js in declarative React components. The minimum setup has three steps:

- **Configure the worker** — Set `pdfjs.GlobalWorkerOptions.workerSrc` at module scope (not inside a component), pointing at `pdfjs-dist/build/pdf.worker.min.mjs` or a CDN equivalent

- **Import the CSS** — `react-pdf/dist/Page/TextLayer.css` and `react-pdf/dist/Page/AnnotationLayer.css` are required for text selection and link annotations

- **Render the document** — Wrap pages in `<Document file={...}><Page pageNumber={1} /></Document>`

The biggest gotcha: The `file` prop uses `===` equality. Object literals like `{ url: "..." }` create a new object every render, triggering infinite refetches. Always wrap them in `useMemo` or `useState`.

If you’d rather skip the wrapper around a wrapper, [Nutrient Web SDK](https://www.nutrient.io/sdk/web-overview/) handles worker setup, text selection, annotations, forms, and signatures with one `NutrientViewer.load()` call.

`react-pdf` is a React wrapper around PDF.js that provides declarative components for rendering PDFs. It trades lower-level control for a simpler, more React-native API.

## Install

Add the package to the project:

```bash

npm install react-pdf

```

## Step 1: Configure the web worker

PDF.js requires a web worker for parsing.

```tsx

import { pdfjs } from "react-pdf";

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

```

Alternatively, load the worker from a CDN:

```tsx

import { pdfjs } from "react-pdf";

pdfjs.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;

```

Content delivery network (CDN) URLs are convenient for prototyping, but they introduce supply-chain risk, can be blocked by strict Content Security Policy (CSP) rules, and add a third-party dependency on your uptime. For production, copy the worker out of `node_modules/pdfjs-dist/build/` with your bundler’s static-copy plugin instead.

**Important:** Set `workerSrc` at the top level of the module, not inside a component. Module execution order matters.

## Step 2: Import required CSS

Add these imports alongside the component imports:

```tsx

import "react-pdf/dist/Page/TextLayer.css";
import "react-pdf/dist/Page/AnnotationLayer.css";

```

These are required for proper text selection and annotation (link) rendering.

## Step 3: Render a PDF

Render a single page with the `Document` and `Page` components:

```tsx

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

function PDFViewer() {
  const [numPages, setNumPages] = useState(null);

  return (
    <Document
      file="https://example.com/document.pdf"
      onLoadSuccess={({ numPages }) => setNumPages(numPages)}
    >
      <Page pageNumber={1} />
    </Document>
  );
}

```

## The file prop

The `file` prop accepts several formats:

```tsx

// URL string.
<Document file="https://example.com/doc.pdf" />

// Imported file.
import samplePDF from "./sample.pdf";
<Document file={samplePDF} />

// File from input.
<Document file={selectedFile} />  // File object from <input type="file">.

// `Uint8Array`.
<Document file={{ data: uint8Array }} />

// URL object form (alternative to the bare string — also accepts `httpHeaders`/`withCredentials` via the separate `options` prop).
<Document file={{ url: "https://example.com/doc.pdf" }} />

```

**Critical:** The `file` prop uses `===` equality checking. If you pass an object literal like `file={{ url: "..." }}`, it creates a new object every render, causing infinite refetches. Always memoize.

```tsx

import { useMemo, useState } from "react";

// Bad — creates a new object every render.
<Document file={{ url: pdfUrl }} />

// Good — memoized.
const file = useMemo(() => ({ url: pdfUrl }), [pdfUrl]);
<Document file={file} />

// Also good — stored in state.
const [file, setFile] = useState({ url: pdfUrl });
<Document file={file} />

```

## Rendering all pages

Loop over `numPages` to render every page in the document:

```tsx

function AllPages() {
  const [numPages, setNumPages] = useState(null);

  return (
    <Document
      file="document.pdf"
      onLoadSuccess={({ numPages }) => setNumPages(numPages)}
    >
      {numPages &&
        Array.from({ length: numPages }, (_, i) => (
          <Page key={`page-${i + 1}`} pageNumber={i + 1} />
        ))}
    </Document>
  );
}

```

## Page sizing

Control page dimensions with `width`, `height`, or `scale`:

```tsx

// Fixed width (height scales proportionally).
<Page pageNumber={1} width={600} />

// Fixed height (only used if width is not set).
<Page pageNumber={1} height={800} />

// Scale factor (1 = 100%, 1.5 = 150%).
<Page pageNumber={1} scale={1.5} />

// Width + scale (width is multiplied by scale).
<Page pageNumber={1} width={600} scale={1.5} />  // Renders at 900px.

```

If both `width` and `height` are provided, `height` is ignored.

## Rotation

Rotate pages globally or per-page:

```tsx

// Global rotation (applies to all pages).
<Document file={file} rotate={90}>
  <Page pageNumber={1} />
</Document>

// Per-page rotation (overrides document rotation).
<Page pageNumber={1} rotate={180} />

```

Valid values: `0`, `90`, `180`, `270`.

## Document-level scale

Set a default scale for all pages:

```tsx

<Document file={file} scale={1.5}>
  <Page pageNumber={1} />  {/* Rendered at 1.5x. */}
  <Page pageNumber={2} />  {/* Also 1.5x. */}
</Document>

```

Per-page `scale` overrides the document-level value.

## Canvas background

Set a custom background color for the canvas:

```tsx

<Page pageNumber={1} canvasBackground="#f5f5f5" />

```

`canvasBackground` accepts any valid `canvas.fillStyle` value.

## Page colors (dark mode)

Invert or customize page colors:

```tsx

<Page
  pageNumber={1}
  pageColors={{
    background: "#1a1a2e",

    foreground: "#e0e0e0",

  }}
/>

```

## Accessing the canvas element

Pass a `canvasRef` to get a direct reference to the rendered canvas element:

```tsx

import { useRef } from "react";

function PageWithCanvasRef() {
  const canvasRef = useRef(null);

  return (
    <Page
      pageNumber={1}
      canvasRef={canvasRef}
      onRenderSuccess={() => {
        // Canvas is ready.
        console.log(canvasRef.current); // HTMLCanvasElement.
      }}
    />
  );
}

```

## Key points

- Configure the worker in the same module where the `react-pdf` components are used.

- Import both CSS files for text selection and annotations to work.

- Memoize the `file` prop — this is the most common `react-pdf` bug.

- Use `width` or `scale` for sizing, not CSS transforms on the canvas.

- `Page` must be a child of `Document` (or receive the `pdf` prop directly).

- Page numbers are 1-indexed (`pageNumber`), while page indices are 0-indexed (`pageIndex`).

## FAQ

#### Why does the <code>file</code> prop trigger infinite refetches?

`react-pdf` uses `===` (referential equality) to decide whether the `file` prop has changed. An object literal like `{ url: pdfUrl }` is a new object on every render, so `react-pdf` thinks the file changed, tears down the previous load, and refetches the same PDF. Wrap the object in `useMemo` or store it in `useState` so the reference is stable.

#### Why does the worker need to be configured at the module level?

`pdfjs.GlobalWorkerOptions.workerSrc` is a global mutation. If you set it inside a component, the first render of any `Document` component might fire before your component runs — and you’ll get a “fake worker” warning at best, or a broken render at worst. Setting `workerSrc` at the top of the module guarantees it’s configured before any PDF.js code runs.

#### What’s the difference between <code>width</code>, <code>height</code>, and <code>scale</code>?

`width` is the rendered pixel width — `height` scales proportionally. `scale` is a multiplier against the PDF’s native dimensions. `height` is only used when neither `width` nor `scale` is set. Combining `width={600}` with `scale={1.5}` renders at 900 pixels — `width` is treated as the base and then scaled. If both `width` and `height` are supplied, `height` is ignored.

#### Why are there two CSS imports?

`TextLayer.css` styles the invisible text layer that overlays each canvas — without it, text selection visually breaks (selections render at the wrong position or with the wrong color). `AnnotationLayer.css` styles link annotations and form fields. If you don’t need text selection or links, you can skip the imports, but most apps want both.

#### Should I use the modern <code>build/</code> worker or <code>legacy/build/</code>?

Use `pdfjs-dist/build/pdf.worker.min.mjs` for modern evergreen browsers. Switch to `pdfjs-dist/legacy/build/pdf.worker.min.mjs` only if you need to support older browsers (Safari < 15.4, Chrome < 95, or anything pre–`top-level await`). The legacy build ships polyfills and is slightly larger.

#### How does Nutrient Web SDK compare for setup?

Nutrient replaces the entire setup with one `NutrientViewer.load({ container, document })` call. No `workerSrc`, no CSS imports, no `file` prop memoization, no `Document`/`Page` component tree. Annotations, forms, signatures, and search all work without additional wiring. See the [migration guide](https://www.nutrient.io/guides/web/about/migration-guides/migrating-from-react-pdf.md) for switching from `react-pdf`.

## How Nutrient Web SDK handles this

All the worker configuration, CSS imports, and prop memoization shown above is unnecessary with Nutrient Web SDK. Here’s the equivalent setup:

```jsx

import { useEffect, useRef } from "react";

function PDFViewer({ document }) {
  const containerRef = useRef(null);

  useEffect(() => {
    const container = containerRef.current;
    let NutrientViewer = null;

    (async () => {
      NutrientViewer = (await import("@nutrient-sdk/viewer")).default;
      NutrientViewer.load({ container, document });
    })();

    return () => {
      NutrientViewer?.unload(container);
    };
  }, [document]);

  return <div ref={containerRef} style={{ height: "100vh" }} />;
}

```

The dynamic `import()` keeps the SDK out of the server-side rendering (SSR) bundle in frameworks like Next.js. `NutrientViewer.unload()` is idempotent and safe to call even if `load()` is still in flight — the SDK handles the load-then-unload race internally.

Nutrient’s WebAssembly (WASM) engine handles text selection, annotations, forms, and signatures directly — no canvas-only fallback, no `file` prop memoization pitfalls, and no separate CSS imports.

---

_See [Nutrient Web SDK](https://www.nutrient.io/sdk/web-overview/) for a setup with no worker configuration, CSS imports, or memoization gotchas. Follow the [migration guide](https://www.nutrient.io/guides/web/about/migration-guides/migrating-from-react-pdf.md) to switch from `react-pdf`, or [talk to Sales](https://www.nutrient.io/contact-sales/?=sdk) about your requirements._
---

## Related pages

- [The business case for accessibility: Five ways it drives enterprise value](/blog/5-ways-accessibility-drives-enterprise-value.md)
- [Accessibility Untangled Why It Matters Guide](/blog/accessibility-untangled-why-it-matters-guide.md)
- [Advanced Techniques For React Native Ui Components](/blog/advanced-techniques-for-react-native-ui-components.md)
- [`vector_store` holds your indexed documents (see the multimodal RAG post](/blog/agentic-rag.md)
- [Ai Document Automation Extraction To Action](/blog/ai-document-automation-extraction-to-action.md)
- [Ai Legal Assistant Document Authoring](/blog/ai-legal-assistant-document-authoring.md)
- [Angular File Viewer Pdf Image Office Files](/blog/angular-file-viewer-pdf-image-office-files.md)
- [Auto Tagging And Document Accessibility In Dotnet Sdk](/blog/auto-tagging-and-document-accessibility-in-dotnet-sdk.md)
- [Best Document Ai Platforms](/blog/best-document-ai-platforms.md)
- [Best Document Viewers](/blog/best-document-viewers.md)
- [Build Vs Buy Document Extraction](/blog/build-vs-buy-document-extraction.md)
- [The CEO’s AI playbook: Why decision architecture beats model selection](/blog/ceo-ai-playbook-decision-architecture.md)
- [1. Extract and chunk the PDF.](/blog/chat-with-pdf.md)
- [Complete Guide To Pdfjs](/blog/complete-guide-to-pdfjs.md)
- [Construction Document Data Extraction](/blog/construction-document-data-extraction.md)
- [Convert One Drive Files To Pdf In Sharepoint](/blog/convert-one-drive-files-to-pdf-in-sharepoint.md)
- [Create And Edit Pdfs In Flutter](/blog/create-and-edit-pdfs-in-flutter.md)
- [Create Pdfs With React](/blog/create-pdfs-with-react.md)
- [Creating A Document Scanner With Ocr In Python](/blog/creating-a-document-scanner-with-ocr-in-python.md)
- [Creating And Filling Pdf Forms Programmatically In Javascript](/blog/creating-and-filling-pdf-forms-programmatically-in-javascript.md)
- [The CTO’s AI playbook: Why accountability architecture beats orchestration](/blog/cto-ai-playbook-accountability-architecture.md)
- [Digital Signatures](/blog/digital-signatures.md)
- [Digital Workflow Automation](/blog/digital-workflow-automation.md)
- [Document Ai Vs Ocr](/blog/document-ai-vs-ocr.md)
- [Document Extraction Confidence Scores](/blog/document-extraction-confidence-scores.md)
- [Document Viewer](/blog/document-viewer.md)
- [Document Watermarking](/blog/document-watermarking.md)
- [Emerging threats: Your logging system may be an agentic threat vector](/blog/emerging-threats-your-logging-system.md)
- [Extract Patient Data On Premises](/blog/extract-patient-data-on-premises.md)
- [app.py](/blog/extract-text-from-pdf-using-python.md)
- [Fillable Pdf](/blog/fillable-pdf.md)
- [How To Add Digital Signature To Pdf Using React](/blog/how-to-add-digital-signature-to-pdf-using-react.md)
- [How To Build A Dotnet Maui Pdf Viewer](/blog/how-to-build-a-dotnet-maui-pdf-viewer.md)
- [How To Build A Flutter Pdf Viewer](/blog/how-to-build-a-flutter-pdf-viewer.md)
- [or](/blog/how-to-build-a-javascript-pdf-viewer-with-pdfjs.md)
- [How To Build A Javascript Pdf Viewer](/blog/how-to-build-a-javascript-pdf-viewer.md)
- [or](/blog/how-to-build-a-nextjs-pdf-viewer.md)
- [How To Build A Powerpoint Viewer Using Javascript](/blog/how-to-build-a-powerpoint-viewer-using-javascript.md)
- [Using Yarn](/blog/how-to-build-a-react-excel-viewer.md)
- [How To Build A React Native Pdf Viewer](/blog/how-to-build-a-react-native-pdf-viewer.md)
- [How To Build A React Powerpoint Viewer](/blog/how-to-build-a-react-powerpoint-viewer.md)
- [How To Build A Reactjs File Viewer](/blog/how-to-build-a-reactjs-file-viewer.md)
- [or](/blog/how-to-build-a-reactjs-pdf-viewer-with-react-pdf.md)
- [or](/blog/how-to-build-a-reactjs-pdf-viewer.md)
- [How To Build A Reactjs Viewer With Pdfjs](/blog/how-to-build-a-reactjs-viewer-with-pdfjs.md)
- [How To Build A Vuejs Pdf Viewer With Pdfjs](/blog/how-to-build-a-vuejs-pdf-viewer-with-pdfjs.md)
- [How To Build A Vuejs Pdf Viewer](/blog/how-to-build-a-vuejs-pdf-viewer.md)
- [How To Build An Android Pdf Viewer](/blog/how-to-build-an-android-pdf-viewer.md)
- [How To Build An Angular Pdf Viewer With Ng2 Pdf Viewer](/blog/how-to-build-an-angular-pdf-viewer-with-ng2-pdf-viewer.md)
- [How To Build An Angular Pdf Viewer With Pdfjs](/blog/how-to-build-an-angular-pdf-viewer-with-pdfjs.md)
- [How To Convert Docx To Pdf Using Javascript](/blog/how-to-convert-docx-to-pdf-using-javascript.md)
- [How To Convert Docx To Pdf Using Python](/blog/how-to-convert-docx-to-pdf-using-python.md)
- [How To Convert Html To Pdf Using Html2pdf](/blog/how-to-convert-html-to-pdf-using-html2pdf.md)
- [or](/blog/how-to-convert-html-to-pdf-using-react.md)
- [How To Convert Html To Pdf Using Wkhtmltopdf And Csharp](/blog/how-to-convert-html-to-pdf-using-wkhtmltopdf-and-csharp.md)
- [or](/blog/how-to-convert-html-to-pdf-using-wkhtmltopdf-and-python.md)
- [How To Convert Word To Pdf In Nodejs](/blog/how-to-convert-word-to-pdf-in-nodejs.md)
- [or](/blog/how-to-create-a-react-js-signature-pad.md)
- [How To Create Pdfs With React To Pdf](/blog/how-to-create-pdfs-with-react-to-pdf.md)
- [How To Edit Pdfs Using Ios Pdf Library](/blog/how-to-edit-pdfs-using-ios-pdf-library.md)
- [How To Embed A Pdf Viewer In Your Website](/blog/how-to-embed-a-pdf-viewer-in-your-website.md)
- [How To Extract Tables From Pdf And Images](/blog/how-to-extract-tables-from-pdf-and-images.md)
- [How To Generate Pdf From Html With Nodejs](/blog/how-to-generate-pdf-from-html-with-nodejs.md)
- [base_url tells WeasyPrint where to resolve relative asset paths](/blog/how-to-generate-pdf-reports-from-html-in-python.md)
- [How To Merge Pdfs Using Javascript](/blog/how-to-merge-pdfs-using-javascript.md)
- [How To Ocr Pdfs In Linux](/blog/how-to-ocr-pdfs-in-linux.md)
- [How To Print Pdf In Csharp](/blog/how-to-print-pdf-in-csharp.md)
- [Open an image.](/blog/how-to-use-tesseract-ocr-in-python.md)
- [From an HTML string.](/blog/html-in-pdf-format.md)
- [Javascript Pdf Editors](/blog/javascript-pdf-editors.md)
- [Javascript Pdf Libraries](/blog/javascript-pdf-libraries.md)
- [Linearized Pdf](/blog/linearized-pdf.md)
- [or](/blog/merge-pdfs.md)
- [Swift Package Manager](/blog/mobile-pdf-sdk.md)
- [`elements` come from your document parser — each has a type and content.](/blog/multimodal-rag.md)
- [Nutrient Vs Conga Composer](/blog/nutrient-vs-conga-composer.md)
- [Online Document Viewer](/blog/online-document-viewer.md)
- [Open Pdf In Your Web App](/blog/open-pdf-in-your-web-app.md)
- [Building WCAG 2.2, Section 508, and PDF/UA-compliant PDFs with an SDK](/blog/pdf-accessibility.md)
- [Pdf Data Extraction Developer Guide](/blog/pdf-data-extraction-developer-guide.md)
- [Pdf Extraction Benchmark Opendataloader Bench](/blog/pdf-extraction-benchmark-opendataloader-bench.md)
- [Pdf Extraction Document Case Studies](/blog/pdf-extraction-document-case-studies.md)
- [Pdf Page Labels](/blog/pdf-page-labels.md)
- [Pdf Sdk Compliance Security Checklist](/blog/pdf-sdk-compliance-security-checklist.md)
- [Pdf Sdk Performance Benchmark](/blog/pdf-sdk-performance-benchmark.md)
- [Pdf Ua Compliance Guide](/blog/pdf-ua-compliance-guide.md)
- [Pdfjs Accessibility Structtree Printing](/blog/pdfjs-accessibility-structtree-printing.md)
- [Pdfjs Advanced Loading Streaming Workers](/blog/pdfjs-advanced-loading-streaming-workers.md)
- [Pdfjs Annotation Editor Layer](/blog/pdfjs-annotation-editor-layer.md)
- [Pdfjs Area Annotations Canvas Capture](/blog/pdfjs-area-annotations-canvas-capture.md)
- [Pdfjs Coordinate Systems Pdf To Screen](/blog/pdfjs-coordinate-systems-pdf-to-screen.md)
- [Pdfjs Document Outline Bookmarks Metadata](/blog/pdfjs-document-outline-bookmarks-metadata.md)
- [Pdfjs Eventbus Guide](/blog/pdfjs-eventbus-guide.md)
- [macOS](/blog/pdfjs-file-format-conversion-to-pdf.md)
- [macOS](/blog/pdfjs-generating-pdf-thumbnails-pdf2pic.md)
- [Pdfjs Limitations Commercial Upgrade](/blog/pdfjs-limitations-commercial-upgrade.md)
- [Pdfjs Native Annotation Layer Forms](/blog/pdfjs-native-annotation-layer-forms.md)
- [Pdfjs Navigation Zoom Rotation](/blog/pdfjs-navigation-zoom-rotation.md)
- [Pdfjs Pdf Page Manipulation Pdf Lib](/blog/pdfjs-pdf-page-manipulation-pdf-lib.md)
- [Pdfjs React Viewer Setup](/blog/pdfjs-react-viewer-setup.md)
- [Pdfjs Rendering Overlays React Portals](/blog/pdfjs-rendering-overlays-react-portals.md)
- [Pdfjs Server Side Text Extraction](/blog/pdfjs-server-side-text-extraction.md)
- [Pdfjs Sticky Note Annotations](/blog/pdfjs-sticky-note-annotations.md)
- [Pdfjs Text Highlight Annotations](/blog/pdfjs-text-highlight-annotations.md)
- [Pdfjs Text Search Pdffindcontroller](/blog/pdfjs-text-search-pdffindcontroller.md)
- [Pdfjs Thumbnail Sidebar](/blog/pdfjs-thumbnail-sidebar.md)
- [Process Flows](/blog/process-flows.md)
- [React Native Pdf Annotation](/blog/react-native-pdf-annotation.md)
- [Using Yarn](/blog/react-pdf-editor.md)
- [or](/blog/sample-blog-updated.md)
- [Sdk Product Updates Q2 2026](/blog/sdk-product-updates-q2-2026.md)
- [Add DWS MCP Server to your Claude Code project.](/blog/teaching-llms-to-read-pdfs.md)
- [Open an image file.](/blog/tesseract-python-guide.md)
- [Define the HTML part of the document.](/blog/top-10-ways-to-generate-pdfs-in-python.md)
- [Top 5 Javascript Pdf Viewers](/blog/top-5-javascript-pdf-viewers.md)
- [or](/blog/top-js-pdf-libraries.md)
- [Convert an HTML file to PDF.](/blog/top-ten-ways-to-convert-html-to-pdf.md)
- [Vector Pdf](/blog/vector-pdf.md)
- [Wcag2 Accessibility Requirements Documents](/blog/wcag2-accessibility-requirements-documents.md)
- [Web Sdk Is Now Headless](/blog/web-sdk-is-now-headless.md)
- [What Are Annotations](/blog/what-are-annotations.md)
- [What Is A Vpat](/blog/what-is-a-vpat.md)
- [What Is Document Processing](/blog/what-is-document-processing.md)
- [What Is Intelligent Document Processing](/blog/what-is-intelligent-document-processing.md)
- [What Is Pdf Ua](/blog/what-is-pdf-ua.md)
- [Why Pdfium Is A Trusted Platform For Pdf Rendering](/blog/why-pdfium-is-a-trusted-platform-for-pdf-rendering.md)
- [Why Your Ai Agent Hallucinates Pdf Table Data](/blog/why-your-ai-agent-hallucinates-pdf-table-data.md)

