react-pdf
React-PDF renders existing PDF documents inside React by wrapping Mozilla PDF.js in Document, Page, Thumbnail, and Outline components. It accepts URLs, files, typed arrays, or PDF.js source objects and can render page canvases plus selectable text and clickable annotation layers. It is a viewer building block, not a complete toolbar and not the similarly named @react-pdf/renderer package used to create PDFs.
React-PDF is a good rendering layer when your product owns the viewer experience. Skip it if you expect installation to produce a complete PDF application or if worker and asset plumbing are out of scope.
Use it if
- You need to display an existing PDF inside a React interface and control the surrounding navigation yourself
- You want page canvases, selectable text, links, thumbnails, or a document outline as React components
- Your PDF source may be a URL, uploaded File, Uint8Array, or authenticated PDF.js request object
- You can own the PDF.js worker and static asset setup in your bundler
- You need to generate a PDF from React components; the README explicitly directs that job to @react-pdf/renderer
- You want a ready-made viewer with search, zoom controls, page virtualization, print, and polished mobile controls; React-PDF supplies rendering components, not that full product shell
- Your framework requires the viewer to render on the server; the README tells Next.js users to skip SSR for the module because PDF.js depends on browser facilities
- You must support old browsers or old iOS versions; current support follows PDF.js, the legacy worker still needs polyfills and transpilation, and the README only reports legacy worker support from iOS 16.4
- You cannot maintain worker, CMap, standard-font, and WebAssembly assets in the build; non-Latin text, older standard fonts, and JPEG 2000 documents may need those resources copied and configured
Setup reality
`npm install react-pdf` installs React-PDF and its pinned `pdfjs-dist` dependency; React 16.8 or newer, React DOM, and matching React types are peers. A basic component still fails until PDF.js can locate its worker. The recommended v10 setup assigns `pdfjs.GlobalWorkerOptions.workerSrc` with `new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString()` in the same module that renders Document or Page. A separate bootstrap file is unsafe because module order can overwrite the value. Next.js must load the viewer with SSR disabled. pnpm may need `pdfjs-dist` hoisted through `.npmrc` on older pnpm or `publicHoistPattern` in `pnpm-workspace.yaml` on pnpm 11+. Parcel needs the `npm:` URL prefix. Text and annotation layers each need their supplied CSS import. PDFs with non-Latin characters may require copying `pdfjs-dist/cmaps`; JPEG 2000 can require the `wasm` directory; old standard-font PDFs may require `standard_fonts`. Those paths go into a stable, memoized Document `options` object. Remote PDFs must allow browser CORS and authenticated requests need a memoized source object. Rendering every page of a long PDF at once can consume substantial CPU and canvas memory, so real viewers usually render only visible pages or thumbnails.
Patterns
Configure the PDF.js worker for v10configure-worker
import { Document, Page, pdfjs } from 'react-pdf';
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/build/pdf.worker.min.mjs',
import.meta.url,
).toString();Put this in the same module that renders Document or Page. A different module can run too early and have its value overwritten.
Load a document and render one pagerender-one-page
import { useState } from 'react';
import { Document, Page } from 'react-pdf';
export function Viewer() {
const [pages, setPages] = useState(0);
return (
<Document file='/manual.pdf' onLoadSuccess={({ numPages }) => setPages(numPages)}>
<Page pageNumber={1} />
<p>{pages ? `1 of ${pages}` : 'Loading...'}</p>
</Document>
);
}Page numbers are one-based. Document loads the file; Page must be nested inside it or receive a `pdf` object explicitly.
Render every page after loadrender-all-pages
const [numPages, setNumPages] = useState(0);
<Document file={file} onLoadSuccess={({ numPages }) => setNumPages(numPages)}>
{Array.from({ length: numPages }, (_, index) => (
<Page key={index + 1} pageNumber={index + 1} />
))}
</Document>This is fine for short files. Long documents need visibility-based rendering or pagination to avoid creating many large canvases at once.
Enable selectable text and annotation linksenable-text-links
import 'react-pdf/dist/Page/TextLayer.css';
import 'react-pdf/dist/Page/AnnotationLayer.css';
<Page
pageNumber={pageNumber}
renderTextLayer
renderAnnotationLayer
externalLinkTarget='_blank'
/>The layers render without useful layout unless both supplied stylesheets are included. External links default to a restrictive rel value.
Load a PDF with request headersload-authenticated-url
const source = useMemo(() => ({
url: 'https://api.example.com/files/report.pdf',
httpHeaders: { Authorization: `Bearer ${token}` },
withCredentials: true,
}), [token]);
<Document file={source} />Memoize object-valued `file` props because React-PDF uses identity checks. The remote server must also permit the browser request through CORS.
Preview a PDF selected by the userload-uploaded-file
const [file, setFile] = useState(null);
<input
type='file'
accept='application/pdf'
onChange={(event) => setFile(event.target.files?.[0] ?? null)}
/>
{file && <Document file={file}><Page pageNumber={1} /></Document>}A browser File can be passed directly. Clear application state when the input is reset so the old document is not left visible.
Render PDF bytes from an API responseload-byte-array
const response = await fetch('/api/report');
if (!response.ok) throw new Error(`PDF request failed: ${response.status}`);
const bytes = new Uint8Array(await response.arrayBuffer());
setSource({ data: bytes });
<Document file={source} />Prefer Uint8Array for binary data. Keep the source object stable between renders or the document will be reloaded.
Size a page to its containerresponsive-page
const [width, setWidth] = useState(600);
<ResizeObserverBox onWidth={setWidth}>
<Page pageNumber={pageNumber} width={Math.min(width, 900)} />
</ResizeObserverBox>When both width and height are supplied, height is ignored. Combining width and scale multiplies the requested width.
Configure CMaps, fonts, and JPEG 2000 supportconfigure-font-assets
const options = {
cMapUrl: '/pdf-assets/cmaps/',
standardFontDataUrl: '/pdf-assets/standard_fonts/',
wasmUrl: '/pdf-assets/wasm/',
};
<Document file={file} options={options} />Copy the matching directories from the installed `pdfjs-dist` version, and define this options object outside the component or memoize it.
Prompt for a protected PDF passwordhandle-password
<Document
file={file}
onPassword={(callback, reason) => {
const password = window.prompt(reason === 1 ? 'Password required' : 'Incorrect password, try again');
callback(password ?? '');
}}
/>The callback must be called with a password attempt. Replace `window.prompt` with accessible product UI in a real application.
Build clickable page thumbnailsrender-thumbnails
import { Document, Thumbnail } from 'react-pdf';
<Document file={file} onLoadSuccess={({ numPages }) => setNumPages(numPages)}>
<div className='thumbnails'>
{Array.from({ length: numPages }, (_, i) => (
<button key={i} onClick={() => setPageNumber(i + 1)}>
<Thumbnail pageNumber={i + 1} width={120} />
</button>
))}
</div>
</Document>Thumbnail omits text and annotation layers, which keeps it lighter than Page but still renders PDF content for every mounted item.
Report document and page failureshandle-load-errors
<Document
file={file}
loading={<p>Loading PDF...</p>}
error={<p>Could not load this PDF.</p>}
onLoadError={(error) => reportError(error)}
>
<Page
pageNumber={pageNumber}
onRenderError={(error) => reportError(error)}
/>
</Document>Document load and Page render are different failure stages. Capture both if telemetry needs to distinguish network, parse, and canvas problems.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| pdfjs-dist | npm | You want direct PDF.js control and are willing to build the React lifecycle and viewer UI yourself |
| @react-pdf-viewer/core | npm | You want a plugin-oriented viewer with packaged controls instead of low-level page components |
| @pdfslick/react | npm | You want a React viewer built around PDF.js with virtualization and a more complete viewing shell |