# React PDF Viewer — the complete documentation

> Accessible, themeable PDF viewer for React, built on PDF.js.

React PDF Viewer is an accessible, themeable PDF viewer for React, built on PDF.js: zoom, search, thumbnails, download and print, with no UI-kit dependency.

Every page of the documentation at https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/, in reading order, with the source of every live example inlined where its page shows it. It is generated from the same Markdown as the site, so it says exactly what the site says.

- Package: `@kingtech/react-pdf-viewer`, documented at version 1.0.1. Peer dependencies: `pdfjs-dist` ^6.0.0, `react` >=18.0.0, `react-dom` >=18.0.0, and nothing else.
- Page index: https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/llms.txt — and any single page as Markdown, at its URL with `.md`.
- 91 pages, 83 examples.

The examples load sample PDFs from the documentation site, such as `/samples/multipage.pdf`; point `source` at your own document.

---

# Overview

> React PDF Viewer is an accessible, themeable PDF viewer for React, built on PDF.js: zoom, search, thumbnails, download and print, with no UI-kit dependency.

Getting started · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/

## Introduction

React PDF Viewer is a React component and a set of building blocks for showing PDF documents in web applications. It renders with [PDF.js](https://github.com/mozilla/pdf.js), Mozilla's PDF engine, and wraps it in a complete, accessible viewer: a toolbar for zoom, page navigation, fullscreen, rotation, download and print, plus opt-in features such as continuous scrolling, thumbnails, search, selectable text and clickable links.

Everything is controlled through props, CSS variables and callbacks, so the viewer fits into your design system and your state management instead of the other way round. When the complete viewer is not the right shape, the same document loader and page renderer are available as a hook and a component.

The package is for viewing. It does not edit, annotate, sign or fill in PDF forms, and it runs in the browser (server rendering outputs the loading state).

## Why React PDF Viewer

- **Accessible:** WAI-ARIA toolbar and menu semantics, keyboard shortcuts, labeled controls, live page announcements and visible focus, checked with axe against WCAG 2.2 AA on every build.
- **Themeable without a UI kit:** plain CSS in a cascade layer with CSS variables for every color and size, plus dark and automatic presets. No MUI, no CSS-in-JS, no icon library.
- **Controlled or not, your choice:** page, zoom, rotation and fullscreen each follow the `x` / `defaultX` / `onXChange` pattern, with an imperative API for one-off commands.
- **Robust rendering:** cancellable renders, aborted loads, typed errors with retry, and sharp output on high-density screens within a canvas memory budget.
- **Production loading:** authenticated requests, custom HTTP clients, local files and bytes, self-hosted PDF.js assets and a strict-CSP-friendly setup.
- **Composable:** replace the toolbar, reuse parts of it, or build your own interface from `usePdfDocument` and `PdfPageCanvas`.

## Start now

---

# Installation

> Install React PDF Viewer and PDF.js with your package manager and import the stylesheet once.

Getting started · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/installation/

## Prerequisites

- React and React DOM 18 or 19.
- `pdfjs-dist` 6 (installed next to the package; it is a peer dependency so you control its version).
- A bundler that supports ES modules and CSS imports, such as Vite, Next.js or webpack 5.

Full details, including tested browsers, are on the [Requirements](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/requirements/) page.

## Installation

Install the package and PDF.js with your package manager.

**npm**

```sh
npm install @kingtech/react-pdf-viewer pdfjs-dist
```

**pnpm**

```sh
pnpm add @kingtech/react-pdf-viewer pdfjs-dist
```

**yarn**

```sh
yarn add @kingtech/react-pdf-viewer pdfjs-dist
```

The package is ESM-only, ships its TypeScript declarations, and has no runtime dependencies of its own.

## Minimal example

Import the component and, once in your app, the stylesheet:

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import '@kingtech/react-pdf-viewer/styles.css';

export function Report() {
  return <PdfViewer source="/files/report.pdf" />;
}
```

`source` can be a URL, a `File`, a `Blob` or bytes; see [Document sources](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/document-sources/).

## Verify

Start your development server and open the page. You should see the first page of the document with a toolbar below it showing the zoom level and `1 / N`. In the browser console, a one-time development notice says PDF.js assets are loading from a CDN; that is expected until you [self-host them](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/).

## Troubleshooting

- **The viewer has no styles.** The stylesheet is imported separately. Add `import '@kingtech/react-pdf-viewer/styles.css'` once, for example in your root layout.
- **“Failed to fetch” or a network error.** The document URL is on another origin without CORS headers. See [Authenticated requests](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/authenticated-requests/#cross-origin-documents).
- **`PDFJS_LOAD_FAILED`.** `pdfjs-dist` is missing or the worker URL is blocked by your Content Security Policy. Install `pdfjs-dist` and see [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/#content-security-policy).

## Next steps

- [Usage](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/usage/): the common props and features.
- [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/): self-host the worker for production.
- Framework setup: [Vite](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/vite/), [Next.js](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/nextjs/) or [webpack](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/webpack/).

---

# Usage

> Render a document, turn on the features you need, and connect the viewer to your app's state.

Getting started · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/usage/

## Prerequisites

The package, `pdfjs-dist` and the stylesheet are installed as described in [Installation](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/installation/).

## Installation

No further setup is needed to follow this page. For production, also [self-host the PDF.js worker](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/).

## Minimal example

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import '@kingtech/react-pdf-viewer/styles.css';

export function Report({ url }: { url: string }) {
  return <PdfViewer source={url} fileName="report.pdf" />;
}
```

Out of the box you get a single-page view with a toolbar below it: zoom in 5% steps between 25% and 500%, page navigation, fullscreen, rotation, download and print. Pages wider than the viewer shrink to fit.

### Turn on features

Each feature beyond the defaults is one prop:

```tsx
<PdfViewer
  source={url}
  layout="continuous" // scroll through all pages
  thumbnails // page sidebar
  search // find bar with highlighting
  pageInput // type a page number
  zoomReset // click the zoom label to reset
  annotationLayer // clickable links
  wheelZoom // Ctrl/⌘ + wheel zoom
  className="rpv-theme-auto" // dark mode that follows the OS
/>
```

Every feature has its own page with live demos: see [All features](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/all-features/).

### Connect to your state

Control any piece of state with `x` / `onXChange`, for example to keep the page in the URL:

```tsx
const [page, setPage] = useState(1);

<PdfViewer source={url} page={page} onPageChange={setPage} />;
```

See [Controlled state](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/controlled-state/) and the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/).

### Handle errors

```tsx
<PdfViewer source={url} onError={(error) => report(error.code, error.message)} />
```

The viewer already shows an error view with Retry; `onError` is for logging. See [Error states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/error-states/).

## Verify

Open the page: the document renders with the features you enabled. Resize the window below 960px to see the compact layout, and narrow the container to watch toolbar actions move into the **More actions** menu.

## Next steps

- [All features](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/all-features/): everything the viewer can do.
- [Customization](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/): make it match your design.
- [Playground](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/demos/playground/): try every option live.

---

# AI context

> The whole documentation and the source of every example in one Markdown file, for AI coding agents.

Getting started · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/ai-context/

## Prerequisites

- An AI coding agent that can read a file in your repository or fetch a URL: Claude Code, Cursor, Copilot, or anything else that takes documentation as context.

## Installation

Download [llms-full.md](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/llms-full.md) into your repository, somewhere your agent reads. A `docs/` folder is the usual place:

```bash
curl -o docs/react-pdf-viewer.md https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/llms-full.md
```

Then tell the agent it is there. For Claude Code, add a line to `CLAUDE.md`; most other agents read `AGENTS.md`:

```md
React PDF Viewer documentation, with the source of every example: docs/react-pdf-viewer.md
```

A tool that indexes documentation by URL can take the URL instead of the file.

## What is in the file

Every page of this documentation in reading order (the guides, every capability page and the whole API reference) with the source of each live example inlined where its page shows it. On the site those examples are running viewers; in the file each one is the code behind it, which is the part an agent can use.

It is generated from the same Markdown as the site, in the same build, so it cannot say anything the site does not. Download it again when you upgrade the package.

## Which file to use

| File                                             | What it holds                                          | Use it when                               |
| ------------------------------------------------ | ------------------------------------------------------ | ----------------------------------------- |
| [llms-full.md](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/llms-full.md)   | Every page and the source of every example             | the agent should know the whole library   |
| [llms-full.txt](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/llms-full.txt) | The same file, under the name tools look for           | a tool asks for a documentation URL       |
| [llms.txt](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/llms.txt)           | An index: one line per page, each linking its Markdown | the agent should fetch only what it needs |
| A page's URL with `.md`                          | That one page as Markdown                              | you are working on a single feature       |

The full file is large, but it fits whole in the context window of a current frontier model. If your tool's window is smaller, give it `llms.txt` and let it fetch the pages it needs.

## Minimal example

With the file in `docs/`, a request like this has everything it needs to be answered from the documentation rather than from guesswork:

```text
Using docs/react-pdf-viewer.md, add a viewer for the invoice PDFs on the order page: fit the
page width, show thumbnails, and send the session token with every request.
```

## Verify

Ask the agent something only the documentation answers: for example, what the viewer reports for an encrypted document when there is no password prompt. An agent reading the file answers `PASSWORD_REQUIRED` and can name the page it came from; one that is not reading it guesses.

## Next steps

- [Installation](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/installation/): the package, `pdfjs-dist` and the stylesheet.
- [Usage](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/usage/): a working viewer and the common props.
- [API reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/): generated from the TypeScript declarations.

---

# Requirements

> The React, PDF.js, bundler and browser versions React PDF Viewer is tested with.

Getting started · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/requirements/

Only versions covered by the project's automated tests are listed here. Other versions may work but are not verified.

## Prerequisites

| Dependency                      | Supported                             | Tested in CI  |
| ------------------------------- | ------------------------------------- | ------------- |
| `react`, `react-dom`            | peer `>=18.0.0`                       | 18.3 and 19.3 |
| `pdfjs-dist`                    | peer `^6.0.0`                         | 6.3           |
| TypeScript (optional)           | declarations target modern TypeScript | 6.0           |
| Node.js (for building your app) | `>=20`                                | 22            |

The package is ESM-only and has no runtime dependencies besides its peers.

### Bundlers and frameworks

Each of these is exercised by a consumer smoke test that installs the packed package into a fresh app and builds it for production:

| Target                                 | Tested version | Guide                                              |
| -------------------------------------- | -------------- | -------------------------------------------------- |
| Vite                                   | 8.3            | [Vite](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/vite/)       |
| Next.js (App Router, server rendering) | 16.3           | [Next.js](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/nextjs/)  |
| webpack                                | 5.111          | [webpack](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/webpack/) |

### Browsers

Browser tests run in the current versions of Chromium, Firefox and WebKit (Playwright engines). PDF.js 6 relies on recent browser APIs, so use current browsers, or the legacy PDF.js build for older ones; see [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/#older-browsers). Microsoft Edge (Chromium) and Safari (WebKit) share those engines but are not tested separately.

## Installation

See [Installation](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/installation/).

## Minimal example

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import '@kingtech/react-pdf-viewer/styles.css';

export const Viewer = () => <PdfViewer source="/files/report.pdf" />;
```

## Verify

Your install matches the supported versions when `npm ls react pdfjs-dist` (or the pnpm/yarn equivalent) lists versions inside the ranges above and the minimal example renders the first page.

## Next steps

- [Installation](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/installation/)
- [Versions](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/versions/) for the support policy
- [Security](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/security/) for the PDF.js version constraints

---

# FAQ

> Answers to common questions about licensing, capabilities, frameworks and troubleshooting.

Getting started · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/faq/

## Licensing

### Is it free for commercial use?

Yes. React PDF Viewer is released under the [MIT License](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/LICENSE): you can use it in commercial and personal projects without a fee. PDF.js, which it uses, is Apache-2.0 licensed; the package's `NOTICE` file lists third-party attributions.

### Do I need to credit the project?

Keep the copyright and license notice when you redistribute the package's code, as the MIT License requires. No credit in your app's UI is needed.

## Capabilities

### Can users edit, annotate, sign or fill in forms?

No. The viewer displays documents. Links are interactive; form fields and other annotations are not. See [Links](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/links/).

### Can it show several pages at once?

`layout="continuous"` scrolls through all pages. For spreads or custom layouts, use the [headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) building blocks.

### Does it work on phones?

Yes. Below 960px the viewer switches to a compact layout, and the toolbar collapses actions into a **More actions** menu when space runs out. See [Toolbar](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/toolbar/).

### Does it work with React Native?

No. It renders with the DOM and canvas APIs of web browsers.

## Frameworks and setup

### Does it work with server rendering?

Yes. The entry points start with `'use client'` and touch no browser globals at import time; the server renders the loading state. See [Next.js](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/nextjs/).

### Why does the console mention a CDN?

Without configuration the PDF.js worker and assets load from jsDelivr, pinned to your installed version. Self-host them for production; see [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/).

## Troubleshooting

### My PDF from another domain doesn't load.

The server must send CORS headers for your origin. See [Authenticated requests](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/authenticated-requests/#cross-origin-documents).

### The document reloads on every render.

A binary source (`Blob`, `File`, `ArrayBuffer`) is being created during render. Keep it in state; see [Document sources](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/document-sources/).

### My labels don't update or re-render constantly.

Define the `labels` object at module scope or memoise it; see [Labels and locale](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/labels/).

## Related

- [Support](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/support/)
- [Requirements](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/requirements/)
- [Best practices](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/best-practices/)

---

# Support

> Where to ask questions, report bugs, request features and disclose security issues.

Getting started · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/support/

React PDF Viewer is free and open source, so support happens in the open on GitHub.

## Free channels

- **Bug reports:** open an [issue](https://github.com/kiralygyula92/react-pdf-viewer/issues/new/choose) with the bug report template. Include the package and `pdfjs-dist` versions, your browser, and a minimal reproduction; a sample PDF helps most.
- **Feature requests:** open an issue with the feature request template and describe the problem you want to solve, not only the solution.
- **Questions:** search existing [issues](https://github.com/kiralygyula92/react-pdf-viewer/issues) first; if nothing matches, open an issue.
- **Contributing:** see [CONTRIBUTING.md](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/CONTRIBUTING.md). Participation follows the [Code of Conduct](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/CODE_OF_CONDUCT.md).

Before reporting, check the [FAQ](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/faq/) and the Limitations section of the feature you are using.

## Reporting a security issue

Please do **not** open a public issue for a vulnerability. Report it privately through [GitHub security advisories](https://github.com/kiralygyula92/react-pdf-viewer/security/advisories/new), with the affected version, the impact and ideally a reproduction (for example a crafted PDF). Vulnerabilities in PDF.js itself should be reported to Mozilla. The full policy is in [SECURITY.md](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/SECURITY.md); see also the [Security guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/security/).

## Related

- [FAQ](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/faq/)
- [Versions](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/versions/)
- [Security guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/security/)

---

# Versions

> Which versions are supported, how versions are numbered, and where documentation for older versions lives.

Getting started · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/versions/

## Supported versions

| Version | Status                         | Documentation |
| ------- | ------------------------------ | ------------- |
| 1.x     | Current (first stable release) | This site     |

Security fixes are released for the latest minor version. Documentation for the current version is always at `/react-pdf-viewer/`.

## Versioning policy

The package follows [Semantic Versioning](https://semver.org/). Every user-facing change is recorded with a [changeset](https://github.com/changesets/changesets) and appears in the [changelog](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/discover-more/changelog/). The versioned API is everything the [API reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/) documents: the package's exports, props, `PdfViewerApi`, labels, the documented CSS classes, data attributes and CSS variables.

- **Patch** (`1.0.x`): bug fixes that do not change the API.
- **Minor** (`1.x.0`): new, backwards-compatible features.
- **Major** (`x.0.0`): breaking changes, each with a guide in [Migration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/migration/).

New behavior is added behind opt-in props, so upgrading keeps existing viewers looking and working the same unless a release says otherwise. Support for a new `pdfjs-dist` major version arrives in a minor release; support for an old one is only dropped in a major release.

## Older versions

There are no older versions yet. When a new major version is released, the documentation for the previous one moves to a permanent versioned address (for example `/react-pdf-viewer/v1/`), and this table links to it. Existing URLs never break.

## Related

- [Changelog](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/discover-more/changelog/)
- [Migration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/migration/)
- [Requirements](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/requirements/)

---

# All features

> Every capability of React PDF Viewer, grouped by what it does, each with its own page and live demos.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/all-features/

React PDF Viewer covers what an application needs to show PDF documents well: loading from any source, reading comfortably on any screen size, finding and selecting text, printing and downloading, and fitting into your app's state, design and accessibility requirements. Everything below is free and open source. Features beyond the classic single-page viewer are opt-in: each is one prop, and turning it on never changes other behavior.

The viewer deliberately does not edit, annotate, sign or fill in documents, and it does not convert other formats to PDF. For needs outside its scope, the [headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) building blocks and the underlying PDF.js document are available.

---

# Document sources

> Open PDFs from a URL, a File the user picked, or bytes you already have, without uploading anything.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/document-sources/

`source` accepts everything a web app usually has at hand when it wants to show a PDF:

- a URL string or `URL` object, fetched by the viewer;
- a `File` or `Blob`, for example from a file input or drag and drop;
- an `ArrayBuffer` or `Uint8Array` you fetched, generated or decrypted yourself.

## Basics

Pass a URL and the viewer fetches, parses and renders the document.

*Example: A URL source* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return <PdfViewer source="/samples/letter-3pages.pdf" />;
}
```

`null`, `undefined` and an empty string show the empty state. Changing `source` aborts the previous request and destroys the previous document, so switching documents quickly never shows a stale page.

### Local files

A `File` is read in the browser and never leaves the user's device.

*Example: A File from an input* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import { useState } from 'react';

export default function Demo() {
  // Keep binary sources in state: they compare by identity.
  const [file, setFile] = useState<File | null>(null);

  return (
    <>
      <p className="demo-controls">
        <label>
          Choose a PDF{' '}
          <input
            type="file"
            accept="application/pdf"
            onChange={(event) => setFile(event.target.files?.[0] ?? null)}
          />
        </label>
      </p>
      <PdfViewer source={file} />
    </>
  );
}
```

### Bytes

`ArrayBuffer` and `Uint8Array` sources are copied before PDF.js takes ownership of them, so the buffer you pass stays usable afterwards.

*Example: An ArrayBuffer source* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import { useEffect, useState } from 'react';

export default function Demo() {
  const [bytes, setBytes] = useState<ArrayBuffer | null>(null);

  useEffect(() => {
    // Bytes you already have: fetched by your own code, generated or decrypted.
    fetch('/samples/mixed-sizes.pdf')
      .then((response) => response.arrayBuffer())
      .then(setBytes);
  }, []);

  // The viewer copies the bytes, so `bytes` stays usable afterwards.
  return <PdfViewer source={bytes} fileName="mixed-sizes.pdf" />;
}
```

> **Warning: Keep binary sources stable**
>
> Strings compare by value, but every other source compares by identity. Creating a `Blob` or
>   `Uint8Array` during render gives the viewer a new source on every render and reloads the document
>   each time. Keep binary sources in state or memoise them.

## Download file name

The download button uses `fileName` when you pass one; otherwise a `File`'s own name, then the last `.pdf` segment of the URL, then `document.pdf`. See [Download](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/download/).

## Customization

`renderEmpty` replaces the view shown while there is no document, for example with a call to action.

*Example: A custom empty state* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return (
    <PdfViewer
      source={null}
      renderEmpty={() => (
        <div role="status" style={{ padding: 32, textAlign: 'center' }}>
          <strong>No report selected</strong>
          <p>Pick a report from the list to preview it here.</p>
        </div>
      )}
    />
  );
}
```

The loading and error views have their own slots; see [Loading states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/loading-states/), [Error states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/error-states/) and the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

To load documents without the viewer, for example to count pages or render thumbnails in your own layout, use [`usePdfDocument`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) directly. It accepts the same sources and request options.

## Limitations

- **Cross-origin URLs need CORS.** The browser blocks reading a PDF from another origin unless the server sends `Access-Control-Allow-Origin`. The viewer reports a `NETWORK_ERROR`. Workaround: enable CORS on the server, or proxy the file through your own origin. See [Authenticated requests](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/authenticated-requests/).
- **Large files are loaded in full.** URL sources are fetched completely before rendering starts (range requests are not used), so a very large PDF takes as long as its download. Workaround: split huge documents server-side, or show your own progress UI with `renderLoading`.
- **Streams are not accepted.** A `ReadableStream` is not a `PdfSource`. Workaround: read it into a `Blob` or `ArrayBuffer` first (`await new Response(stream).blob()`).

## API

- [PdfViewer](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer/)
- [PdfSource](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-source/)

---

# Page navigation

> Move between pages with the toolbar, the keyboard or your own controls, and keep the current page in sync with your app.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/page-navigation/

## Basics

The toolbar shows the current page and total, with previous and next buttons that disable at the ends.

*Example: Toolbar navigation* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return <PdfViewer source="/samples/multipage.pdf" />;
}
```

The page label is a polite live region, so screen readers announce page changes. With focus inside the viewer, <kbd>←</kbd> <kbd>→</kbd>, <kbd>PageUp</kbd> <kbd>PageDown</kbd> and <kbd>Home</kbd> <kbd>End</kbd> also navigate; see [Keyboard shortcuts](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/keyboard-shortcuts/).

### Controlled page

Pass `page` and `onPageChange` to drive navigation from your app, for example from a table of contents or the URL.

*Example: A table of contents controls the page* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import { useState } from 'react';

const CHAPTERS = [
  { title: 'Introduction', page: 1 },
  { title: 'Chapter 2', page: 11 },
  { title: 'Chapter 3', page: 21 },
  { title: 'Appendix', page: 40 },
];

export default function Demo() {
  const [page, setPage] = useState(1);

  return (
    <>
      <nav className="demo-controls" aria-label="Chapters">
        {CHAPTERS.map((chapter) => (
          <button
            key={chapter.page}
            type="button"
            className="demo-button"
            aria-current={page >= chapter.page ? 'true' : undefined}
            onClick={() => setPage(chapter.page)}
          >
            {chapter.title}
          </button>
        ))}
        <output>Page {page}</output>
      </nav>
      <PdfViewer source="/samples/multipage.pdf" page={page} onPageChange={setPage} />
    </>
  );
}
```

Out-of-range values are clamped: `page={99}` on a 40-page document shows page 40 and calls `onPageChange(40)` once. For the full pattern see [Controlled state](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/controlled-state/).

### Initial page

Uncontrolled viewers start on `defaultPage` (default `1`), and every new `source` opens on `defaultPage` again.

```tsx
<PdfViewer source={report} defaultPage={3} />
```

## Deep links

Keep the page in the address bar so every view is shareable: control `page` (and `scale` or `rotation` if you like) from the query string, and write changes back in `onPageChange`. See [Controlled state](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/controlled-state/).

## Customization

Rename the controls and the page label through `labels`, and restyle the toolbar with CSS variables.

*Example: Custom labels and toolbar color* — the source of the live demo on this page.

```tsx
import { PdfViewer, type PdfViewerLabels } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

// Module scope keeps the object stable between renders.
const labels: Partial<PdfViewerLabels> = {
  previousPage: 'Previous sheet',
  nextPage: 'Next sheet',
  pageIndicator: (page, total, { formatNumber }) =>
    `Sheet ${formatNumber(page)} of ${formatNumber(total)}`,
};

const style = {
  '--rpv-toolbar-bg': '#0f766e',
  '--rpv-label-font-weight': '500',
} as CSSProperties;

export default function Demo() {
  return <PdfViewer source="/samples/multipage.pdf" labels={labels} style={style} />;
}
```

To let users type a page number, turn on [`pageInput`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/page-number-input/). More options: the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

`goToPage`, `nextPage` and `previousPage` on the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/) navigate without controlling the page, which is convenient for buttons outside the viewer.

## Limitations

- **One page at a time by default.** The default layout shows a single page. Workaround: set `layout="continuous"` for scrolling through all pages; see [Continuous layout](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/continuous-layout/).
- **No outline (bookmarks) panel.** The document outline embedded in some PDFs is not shown. Workaround: build a table of contents from `getDocument().getOutline()` on the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/) and drive `page` from it.

## API

- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Zoom

> Zoom in steps or through presets, with the toolbar, the keyboard or Ctrl/⌘ + wheel, between limits you choose.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/zoom/

## Basics

The zoom buttons change the scale in 5% steps between 25% and 500%; the label shows the rounded percentage.

*Example: Default zoom controls* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return <PdfViewer source="/samples/letter-3pages.pdf" />;
}
```

At 100% a page is shown at its true size (one PDF point per CSS pixel). While at the default scale, a page wider than the viewer is shrunk to fit; once zoomed, it keeps its size and the document area scrolls. <kbd>+</kbd>, <kbd>-</kbd> and <kbd>0</kbd> zoom from the keyboard.

### Presets

`zoomLevels` replaces the fixed step with a ladder of presets. `zoomReset` turns the percentage into a button that returns to `defaultScale`.

*Example: Zoom presets with a reset button* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

const ZOOM_LEVELS = [0.5, 0.75, 1, 1.5, 2, 3];

export default function Demo() {
  return (
    <PdfViewer
      source="/samples/letter-3pages.pdf"
      zoomLevels={ZOOM_LEVELS}
      minScale={0.5}
      maxScale={3}
      zoomReset
    />
  );
}
```

### Wheel and pinch

`wheelZoom` zooms with <kbd>Ctrl</kbd>/<kbd>⌘</kbd> + mouse wheel and trackpad pinch, anchored at the pointer. A plain wheel still scrolls the page.

*Example: Ctrl/⌘ + wheel zoom* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // Hold Ctrl (⌘ on macOS) and scroll, or pinch on a trackpad, over the page.
  return <PdfViewer source="/samples/letter-3pages.pdf" wheelZoom zoomReset />;
}
```

## Controlled zoom

`scale` and `onScaleChange` put the zoom level in your state, for example to remember it per user. Values outside `[minScale, maxScale]` are clamped.

```tsx
const [scale, setScale] = useState(1.25);

<PdfViewer source={url} scale={scale} onScaleChange={setScale} />;
```

## Customization

Change the step with `scaleStep`, the wording with `labels`, and the look with CSS variables.

*Example: 10% steps, custom labels and larger buttons* — the source of the live demo on this page.

```tsx
import { PdfViewer, type PdfViewerLabels } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

const labels: Partial<PdfViewerLabels> = {
  zoomLevel: (percent) => `${percent} %`,
  zoomIn: 'Enlarge',
  zoomOut: 'Reduce',
};

const style = {
  '--rpv-toolbar-bg': '#1e293b',
  '--rpv-label-font-size': '13px',
  '--rpv-button-size': '38px',
} as CSSProperties;

export default function Demo() {
  return (
    <PdfViewer source="/samples/letter-3pages.pdf" scaleStep={0.1} labels={labels} style={style} />
  );
}
```

See the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/) and the full list of [CSS variables](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/css-variables/).

## Advanced / escape hatch

`setScale(scale)`, `zoomIn()`, `zoomOut()` and `resetZoom()` on the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/) zoom from your own controls; `canZoomIn` and `canZoomOut` tell you when to disable them. To fit the page to the viewer instead of choosing a percentage, see [Fit modes](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/fit-modes/).

## Limitations

- **Very high zoom is capped by the canvas budget.** Above `maxCanvasPixels` (16,777,216 pixels per page by default) the page is rendered at a lower resolution and scaled up, so it looks softer. Workaround: raise `maxCanvasPixels` on desktop-only apps, or lower `maxScale`. See [Rendering](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/rendering/).
- **Browser zoom is separate.** Ctrl/⌘ + wheel zooms the page only when `wheelZoom` is on and the pointer is over the document; elsewhere the browser zooms the whole page as usual.

## API

- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Rotation

> Rotate pages in quarter turns, on top of the orientation each page already declares.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/rotation/

## Basics

The rotate button turns every page 90° clockwise; pages that declare their own rotation open upright.

*Example: A document with intrinsically rotated pages* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // Page 2 of this sample has an intrinsic /Rotate 90: it opens in landscape.
  return <PdfViewer source="/samples/intrinsic-rotation.pdf" />;
}
```

Rotation is added to each page's intrinsic `/Rotate` value, so a scanned landscape page stays landscape. <kbd>r</kbd> and <kbd>Shift</kbd> + <kbd>R</kbd> rotate clockwise and counter-clockwise from the keyboard.

### Controlled rotation

`rotation` and `onRotationChange` accept `0`, `90`, `180` or `270`.

*Example: Rotate in both directions from outside* — the source of the live demo on this page.

```tsx
import { PdfViewer, type Rotation } from '@kingtech/react-pdf-viewer';
import { useState } from 'react';

const turn = (rotation: Rotation, quarters: number) =>
  ((((rotation + quarters * 90) % 360) + 360) % 360) as Rotation;

export default function Demo() {
  const [rotation, setRotation] = useState<Rotation>(90);

  return (
    <>
      <p className="demo-controls">
        <button
          type="button"
          className="demo-button"
          onClick={() => setRotation((r) => turn(r, -1))}
        >
          ↺ Counter-clockwise
        </button>
        <button
          type="button"
          className="demo-button"
          onClick={() => setRotation((r) => turn(r, 1))}
        >
          ↻ Clockwise
        </button>
        <output>{rotation}°</output>
      </p>
      <PdfViewer
        source="/samples/letter-3pages.pdf"
        rotation={rotation}
        onRotationChange={setRotation}
      />
    </>
  );
}
```

## Customization

Start rotated with `defaultRotation`, rename the button with `labels.rotate`, or choose which actions the toolbar shows.

*Example: Start upside down with a renamed button* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return (
    <PdfViewer
      source="/samples/letter-3pages.pdf"
      defaultRotation={180}
      labels={{ rotate: 'Turn page' }}
      toolbar={{ actions: ['previousPage', 'pageIndicator', 'nextPage', 'rotate'] }}
    />
  );
}
```

See the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

`rotate('cw')` and `rotate('ccw')` on the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/) rotate without controlling the value. `PdfPageCanvas` takes the same `rotation` prop in [headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) layouts.

## Limitations

- **Rotation applies to the whole document.** Every page turns by the same amount. Workaround: render individual pages with [`PdfPageCanvas`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/), each with its own `rotation`.
- **Hidden in compact layouts.** Below `compactBreakpoint` the rotate button is hidden outside fullscreen, as it is by default in `hiddenWhenCompact`. Workaround: pass `toolbar={{ hiddenWhenCompact: [] }}`; see [Toolbar](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/toolbar/).
- **Rotation is not saved into the file.** Downloads and prints use the original page orientation.

## API

- [Rotation](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/rotation/)
- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Download

> Save the open document under a sensible file name without requesting it again, or take over the download yourself.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/download/

## Basics

The download button saves the bytes that are already loaded, so authenticated or expiring URLs are never requested twice.

*Example: Download from the toolbar* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // The download button saves the bytes already loaded: no second request.
  return <PdfViewer source="/samples/letter-3pages.pdf" />;
}
```

The file name is `fileName` when you pass one; otherwise a `File`'s name, the last `.pdf` segment of the URL, or `document.pdf`.

### Intercepting the download

`onDownload` receives `{ fileName, data }` before anything is saved. Return `false` to handle the bytes yourself.

*Example: Intercept the download* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import { useState } from 'react';

export default function Demo() {
  const [message, setMessage] = useState('Click the download button in the toolbar.');

  return (
    <>
      <p className="demo-output" role="status">
        {message}
      </p>
      <PdfViewer
        source="/samples/letter-3pages.pdf"
        onDownload={({ fileName, data }) => {
          // Upload, log or post-process the bytes instead of saving them.
          setMessage(`Intercepted ${fileName}: ${data.byteLength.toLocaleString()} bytes.`);
          return false;
        }}
      />
    </>
  );
}
```

If saving fails, `onError` receives a `DOWNLOAD_FAILED` error; see [Error states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/error-states/).

## Customization

Set the file name, rename the button, and choose which toolbar actions appear.

*Example: A custom file name and button label* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return (
    <PdfViewer
      source="/samples/letter-3pages.pdf"
      fileName="quarterly-summary.pdf"
      labels={{ download: 'Save a copy' }}
      toolbar={{ actions: ['zoomOut', 'zoomLevel', 'zoomIn', 'download'] }}
    />
  );
}
```

See the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

`download()` on the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/) starts the same download from your own button. `getDocument()` returns the PDF.js document if you need its `getData()` for something other than saving.

## Limitations

- **The original file is saved.** Rotation, zoom and search highlights are not written into the download. Workaround: none within the viewer; post-process the bytes from `onDownload` with a PDF library if you need a modified file.
- **Some mobile browsers open instead of saving.** iOS Safari may show the PDF in a new view rather than a download prompt; this is browser behavior. Workaround: none reliable; the user can share or save from there.

## API

- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Print

> Print the document at 150 DPI with progress and a cancel button, or hand printing to the browser's own PDF viewer.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/print/

## Basics

The print button renders every page at 150 DPI into a hidden frame, then opens the browser's print dialog.

*Example: Print a 40-page document* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // Print renders every page into a hidden iframe, then opens the print dialog.
  return <PdfViewer source="/samples/multipage.pdf" />;
}
```

While pages are prepared, a status message shows the progress with a Cancel button, so long documents never look frozen. Failures reach `onError` as `PRINT_FAILED`. Each page prints on a sheet of its own size, so a document that mixes page sizes prints correctly.

### Opening the PDF instead

`printMode="open-url"` opens the document in a new tab and lets the browser's built-in viewer print it.

*Example: Open the PDF in a new tab to print* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // Opens the PDF in a new tab and leaves printing to the browser's own viewer.
  return <PdfViewer source="/samples/letter-3pages.pdf" printMode="open-url" />;
}
```

### Intercepting print

`onPrint` runs before printing starts. Return `false` to print your own way, for example through a server-generated copy.

```tsx
<PdfViewer
  source={url}
  onPrint={() => {
    window.open(`/reports/${id}/print`, '_blank');
    return false;
  }}
/>
```

## Customization

Rename the button and the progress message through `labels`.

*Example: Custom print labels* — the source of the live demo on this page.

```tsx
import { PdfViewer, type PdfViewerLabels } from '@kingtech/react-pdf-viewer';

const labels: Partial<PdfViewerLabels> = {
  print: 'Print document',
  printProgress: (done, total) => `Preparing page ${done} of ${total}…`,
  cancel: 'Stop',
};

export default function Demo() {
  return <PdfViewer source="/samples/multipage.pdf" labels={labels} />;
}
```

See the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

`print()` on the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/) prints from your own controls; its promise resolves after the print dialog closes.

## Limitations

- **The print dialog itself cannot be canceled by the app.** Cancel stops page preparation; once the browser's dialog is open, it belongs to the browser.
- **Printing is rasterised.** Pages are rendered to images for printing, so text is not selectable in a printed-to-PDF copy. Workaround: `printMode="open-url"` prints through the browser's viewer, which keeps vector text.
- **Hidden in compact layouts.** Below `compactBreakpoint` the print button is hidden outside fullscreen by default. Workaround: `toolbar={{ hiddenWhenCompact: [] }}`.
- **Prepared pages are held in memory.** Every page is kept as an image until the dialog closes, so printing a very long document needs a lot of memory. Workaround: `printMode="open-url"`, which hands the document to the browser.
- **Sheet sizes need named-page support.** Browsers without CSS named pages print every sheet at the first page's size (pages are scaled to fit, never cut).
- **`open-url` needs a plain URL.** Only an http(s) URL loaded without `fetcher` or `requestInit` opens as is, because a new tab would not send those headers. Binary sources and every other URL open as a temporary object URL, which some pop-up blockers block. Workaround: use the default `render` mode.

## API

- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Error states

> Show a clear message with Retry when a document can't load or render, and receive a typed error for your own handling.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/error-states/

## Basics

When a document fails to load, the viewer shows an alert with a Retry button and keeps the toolbar usable.

*Example: A missing file* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return <PdfViewer source="/samples/does-not-exist.pdf" />;
}
```

The toolbar stays so that fullscreen can always be left. Aborted loads and canceled renders are never reported as errors.

### Failure types

Every failure reaches `onError` as a `PdfViewerError` with a machine-readable `code`, a readable `message`, the HTTP `status` where there is one, and the original `cause`.

*Example: Each failure and its onError payload* — the source of the live demo on this page.

```tsx
import { PdfViewer, type PdfViewerError } from '@kingtech/react-pdf-viewer';
import { useState } from 'react';

const CASES = {
  'Missing file (404)': '/samples/does-not-exist.pdf',
  'Not a PDF': '/samples/not-a-pdf.pdf',
  'Password protected': '/samples/password.pdf',
  'Blocked by CORS': 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
};

type Case = keyof typeof CASES;

export default function Demo() {
  const [current, setCurrent] = useState<Case>('Missing file (404)');
  const [error, setError] = useState<PdfViewerError | null>(null);

  return (
    <>
      <p className="demo-controls">
        <label>
          Failure{' '}
          <select
            value={current}
            onChange={(event) => {
              setCurrent(event.target.value as Case);
              setError(null);
            }}
          >
            {Object.keys(CASES).map((name) => (
              <option key={name}>{name}</option>
            ))}
          </select>
        </label>
      </p>
      <PdfViewer key={current} source={CASES[current]} onError={setError} />
      <pre className="demo-output" aria-label="onError payload">
        {error
          ? JSON.stringify(
              { code: error.code, status: error.status, message: error.message },
              null,
              2,
            )
          : 'onError not called yet'}
      </pre>
    </>
  );
}
```

`RENDER_FAILED` replaces only the page that failed; the password codes are covered in [Password protection](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/password-protection/).

## Reporting errors

```tsx
<PdfViewer
  source={url}
  onError={(error) =>
    logger.warn('pdf-viewer', error.code, { status: error.status, cause: error.cause })
  }
/>
```

HTTP errors that `onHttpError` handles (for example a 401 that redirects to sign-in) are not reported; see [Authenticated requests](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/authenticated-requests/).

## Customization

`renderError(error, { retry })` replaces the error view for document and page errors.

*Example: A custom error view* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return (
    <PdfViewer
      source="/samples/does-not-exist.pdf"
      renderError={(error, { retry }) => (
        <div role="alert" style={{ padding: 24, border: '1px solid #fca5a5', borderRadius: 12 }}>
          <strong>We couldn’t open this document.</strong>
          <p>
            {error.code === 'HTTP_ERROR' && error.status === 404
              ? 'It may have been moved or deleted.'
              : error.message}
          </p>
          <button type="button" onClick={retry}>
            Try again
          </button>
        </div>
      )}
    />
  );
}
```

Colors of the default view are CSS variables (`--rpv-error-bg`, `--rpv-error-fg`, `--rpv-error-icon`); see the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

`reload()` on the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/) retries from anywhere in your app. With [`usePdfDocument`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/), `error` and `reload` are returned directly.

## Limitations

- **Messages are in English by default.** `HTTP_ERROR` messages come from `getHttpErrorMessage`, which reads RFC 7807 bodies; other codes carry English messages. Workaround: map `error.code` to your own text in `renderError`.
- **CORS failures are indistinguishable from offline.** Browsers do not tell scripts why a cross-origin request failed, so both appear as `NETWORK_ERROR`. Workaround: explain both possibilities in your error view, as the demo above does.

## API

- [PdfViewerError](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-error/)
- [PdfViewerErrorCode](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-error-code/)

---

# Toolbar

> Place the toolbar above or below the page, choose its actions, and let it collapse into a “More actions” menu on narrow screens.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/toolbar/

## Basics

`toolbar={{ position: 'top' }}` moves the toolbar above the page; the default is below it.

*Example: Toolbar above the page* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return <PdfViewer source="/samples/letter-3pages.pdf" toolbar={{ position: 'top' }} />;
}
```

The toolbar groups its controls into zoom, page navigation and actions. It is a single tab stop: <kbd>Tab</kbd> enters it and <kbd>←</kbd> <kbd>→</kbd> <kbd>Home</kbd> <kbd>End</kbd> move between controls. `toolbar={false}` hides it.

### Choosing actions

`actions` lists the controls to show. `hiddenWhenCompact` lists those hidden below `compactBreakpoint` (960px by default) outside fullscreen; by default that is rotate and print.

*Example: Navigation, zoom and download only* — the source of the live demo on this page.

```tsx
import { PdfViewer, type ToolbarConfig } from '@kingtech/react-pdf-viewer';

const toolbar: ToolbarConfig = {
  actions: [
    'previousPage',
    'pageIndicator',
    'nextPage',
    'zoomOut',
    'zoomLevel',
    'zoomIn',
    'download',
  ],
  hiddenWhenCompact: ['download'],
};

export default function Demo() {
  return <PdfViewer source="/samples/multipage.pdf" toolbar={toolbar} />;
}
```

The order of controls within each group is fixed, so every toolbar reads the same way. The available actions are listed in the [`ToolbarAction` reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/toolbar-action/).

### Responsive overflow

The toolbar is always a single row. When it does not fit its container, the actions (fullscreen, rotate, download, print) move into a **⋮ More actions** menu; on very narrow viewers the zoom controls follow. Page navigation always stays visible.

*Example: Drag the width to watch the toolbar collapse* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import { useState } from 'react';

export default function Demo() {
  const [width, setWidth] = useState(420);

  return (
    <>
      <p className="demo-controls">
        <label>
          Container width: {width}px{' '}
          <input
            type="range"
            min={320}
            max={720}
            step={10}
            value={width}
            onChange={(event) => setWidth(Number(event.target.value))}
          />
        </label>
      </p>
      <div style={{ width, maxWidth: '100%', margin: '0 auto' }}>
        <PdfViewer source="/samples/letter-3pages.pdf" pageInput zoomReset />
      </div>
    </>
  );
}
```

The menu follows the WAI-ARIA menu button pattern (<kbd>Enter</kbd> or <kbd>↓</kbd> opens it, arrows move, <kbd>Esc</kbd> closes it and returns focus) and opens away from the edge the toolbar sits on.

## Customization

Colors, radius, spacing, button and icon sizes, and the menu surface are CSS variables.

*Example: A pill-shaped purple toolbar* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

const style = {
  '--rpv-toolbar-bg': '#4c1d95',
  '--rpv-toolbar-radius': '999px',
  '--rpv-toolbar-padding-x': '24px',
  '--rpv-button-hover-bg': 'rgba(255, 255, 255, 0.18)',
  '--rpv-menu-bg': '#faf5ff',
} as CSSProperties;

export default function Demo() {
  return <PdfViewer source="/samples/letter-3pages.pdf" style={style} />;
}
```

All toolbar variables (`--rpv-toolbar-*`, `--rpv-button-*`, `--rpv-menu-*`) are listed in the [CSS variables reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/css-variables/); see also the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

To replace the toolbar with your own UI, or place parts of the default toolbar somewhere else, use [`renderToolbar`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar/) with [`PdfToolbar`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-toolbar/).

## Limitations

- **Controls cannot be reordered within a group.** `actions` selects controls but keeps their order. Workaround: build your own toolbar with [`renderToolbar`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar/).
- **The compact layout follows the window width, not the container.** `compactBreakpoint` compares with the viewport, while the overflow menu reacts to the container. Workaround: set `compactBreakpoint={0}` in narrow containers on wide screens and rely on the overflow menu.
- **No custom buttons in the default toolbar.** Workaround: render `PdfToolbar` next to your own buttons inside `renderToolbar`.

## API

- [ToolbarConfig](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/toolbar-config/)
- [ToolbarAction](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/toolbar-action/)

---

# Fit modes

> Size pages to the available width, to the whole page, or to their true size, and switch to true size as soon as users zoom.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/fit-modes/

## Basics

`fitMode="width"` scales pages to fill the width of the document area at the default scale.

*Example: Fill the width* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // At the default scale the page fills the available width.
  return <PdfViewer source="/samples/letter-3pages.pdf" fitMode="width" />;
}
```

Fitting applies only while the scale equals `defaultScale`. As soon as the user zooms, the page is shown at its true size and the document area scrolls. Reset zoom (<kbd>0</kbd>) to fit again.

### Fit the whole page

`fitMode="page"` scales each page so it fits entirely, width and height, which suits slide decks and documents with mixed page sizes.

*Example: Fit each page entirely* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // Pages of different sizes each fit entirely inside the document area.
  return <PdfViewer source="/samples/mixed-sizes.pdf" fitMode="page" />;
}
```

### Shrink only, or true size

Without `fitMode`, a page wider than the document area is shrunk to fit but never enlarged (`fitWidthAtDefaultScale`, on by default). Turn it off to always show pages at their true size.

*Example: Always true size (A3 landscape page)* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // Never shrink: 100% is always the page's true size, and wide pages scroll.
  return (
    <PdfViewer source="/samples/mixed-sizes.pdf" defaultPage={3} fitWidthAtDefaultScale={false} />
  );
}
```

## Choosing a mode

| You want                                       | Use                              |
| ---------------------------------------------- | -------------------------------- |
| Readable text on phones and in narrow columns  | the default (shrink only)        |
| Documents that use all the space you give them | `fitMode="width"`                |
| Presentations, posters, mixed page sizes       | `fitMode="page"`                 |
| Pixel-exact 100% (e.g. comparing prints)       | `fitWidthAtDefaultScale={false}` |

## Customization

The size of the document area is set by CSS variables, so the same fit mode behaves differently in different layouts.

*Example: A wide, short document area* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

// Fit into a wider, shorter document area than the default.
const style = {
  '--rpv-viewport-max-width': '900px',
  '--rpv-viewport-min-height': '360px',
  '--rpv-viewport-max-height': '360px',
  '--rpv-page-radius': '0px',
} as CSSProperties;

export default function Demo() {
  return (
    <PdfViewer source="/samples/mixed-sizes.pdf" defaultPage={3} fitMode="page" style={style} />
  );
}
```

See `--rpv-viewport-max-width`, `--rpv-viewport-min-height` and `--rpv-viewport-max-height` in the [CSS variables reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/css-variables/) and the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

[`PdfPageCanvas`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) takes a `fit` box (`{ width, height, upscale }`) so custom layouts can size pages to any container; see [`FitOptions`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/fit-options/).

## Limitations

- **Fitting only happens at the default scale.** A controlled `scale` that differs from `defaultScale` always shows true size. Workaround: set `defaultScale` to the value you control, or reset to it.
- **Page fit uses the document area's height.** Inline, that area is at most 800px tall (100% of the viewer in fullscreen), so a tall portrait page fits to that height. Workaround: set `--rpv-viewport-max-height` to the height you want to fill.

## API

- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Fullscreen

> Show the document fullscreen with the browser's Fullscreen API, an in-page overlay, or inside your own dialog.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/fullscreen/

## Basics

The fullscreen button puts the viewer into browser fullscreen; <kbd>Esc</kbd> or the button leaves it.

*Example: Native fullscreen* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // The fullscreen button uses the browser Fullscreen API; Esc leaves it.
  return <PdfViewer source="/samples/letter-3pages.pdf" />;
}
```

The button's label and icon follow the state, and the document is never reloaded when fullscreen changes. <kbd>f</kbd> toggles fullscreen from the keyboard.

### Overlay

`fullscreenMode="overlay"` covers the browser window with a fixed layer instead. Focus stays inside the viewer, the page behind stops scrolling, and <kbd>Esc</kbd> closes it.

*Example: Overlay fullscreen* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // A full-viewport layer inside the page: focus is trapped, the page behind stops scrolling.
  return <PdfViewer source="/samples/letter-3pages.pdf" fullscreenMode="overlay" />;
}
```

Where element fullscreen is unavailable, `native` mode falls back to the overlay automatically.

### Controlled by your app

Passing `fullscreen` selects `fullscreenMode="controlled"`: the viewer switches to its fullscreen layout and your component decides how to present it, for example in your own dialog.

*Example: Fullscreen inside your own dialog* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import { useState } from 'react';

export default function Demo() {
  const [fullscreen, setFullscreen] = useState(false);

  const viewer = (
    <PdfViewer
      source="/samples/letter-3pages.pdf"
      fullscreen={fullscreen}
      onFullscreenChange={setFullscreen}
    />
  );

  // `fullscreen` is passed, so the mode is "controlled": the viewer switches to its fullscreen
  // layout and this component decides how to present it. The viewer stays mounted.
  return fullscreen ? (
    <div
      role="dialog"
      aria-modal="true"
      aria-label="Document"
      style={{ position: 'fixed', inset: 0, zIndex: 1000, background: '#fff', overflowY: 'auto' }}
    >
      {viewer}
    </div>
  ) : (
    viewer
  );
}
```

| Mode                                     | Who presents fullscreen         | Use when                                                 |
| ---------------------------------------- | ------------------------------- | -------------------------------------------------------- |
| `native` (default without `fullscreen`)  | The browser                     | You want real fullscreen                                 |
| `overlay`                                | The viewer, as an in-page layer | Embedded pages, or iframes without fullscreen permission |
| `controlled` (default with `fullscreen`) | Your app                        | Your design system already has a full-screen dialog      |

## Customization

The fullscreen background, padding and stacking order are CSS variables, and both button labels can be renamed.

*Example: A dark “focus mode”* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

const style = {
  '--rpv-fullscreen-bg': '#0f172a',
  '--rpv-toolbar-bg': '#334155',
  '--rpv-viewport-padding-fullscreen': '48px',
} as CSSProperties;

export default function Demo() {
  return (
    <PdfViewer
      source="/samples/letter-3pages.pdf"
      fullscreenMode="overlay"
      labels={{ enterFullscreen: 'Focus mode', exitFullscreen: 'Leave focus mode' }}
      style={style}
    />
  );
}
```

See `--rpv-fullscreen-bg`, `--rpv-viewport-padding-fullscreen` and `--rpv-overlay-z-index` in the [CSS variables reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/css-variables/) and the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

`toggleFullscreen()` and the `fullscreen` value on the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/) work in every mode. `onFullscreenChange` reports changes made by the browser too, such as pressing <kbd>Esc</kbd> in native fullscreen.

## Limitations

- **Native fullscreen needs permission inside iframes.** A cross-origin iframe must allow it (`allow="fullscreen"`); otherwise the viewer falls back to the overlay, which covers only the iframe. Workaround: add the `allow` attribute, or open the document in a top-level page.
- **The overlay is confined by transformed ancestors.** A CSS `transform`, `filter` or `contain` on an ancestor makes the fixed layer cover that ancestor instead of the window. Workaround: use `controlled` mode and render your dialog outside the transformed element.
- **Some mobile browsers do not support element fullscreen.** The viewer falls back to the overlay where the Fullscreen API is unavailable; this fallback has not been verified on every mobile browser.

## API

- [FullscreenMode](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/fullscreen-mode/)
- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Continuous layout

> Scroll through every page in one column, rendering only the pages near the screen so long documents stay fast.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/continuous-layout/

## Basics

`layout="continuous"` shows all pages in a vertical, scrollable column.

*Example: A 40-page document, scrolling* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return <PdfViewer source="/samples/multipage.pdf" layout="continuous" />;
}
```

Every page reserves its space immediately, so the scrollbar is accurate from the start, but only the visible pages and one page on each side are rendered to a canvas. The page indicator follows the scroll position, and previous and next scroll to the neighbouring page.

### With thumbnails and page input

Continuous layout pairs well with [thumbnails](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/thumbnails/) and the [page number input](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/page-number-input/) for long documents.

*Example: Continuous layout, thumbnails and page input* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return <PdfViewer source="/samples/multipage.pdf" layout="continuous" thumbnails pageInput />;
}
```

## Customization

The gap between pages and the page frame are CSS variables.

*Example: Wider gaps and softer page shadows* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

const style = {
  '--rpv-page-gap': '40px',
  '--rpv-page-shadow': '0 12px 32px rgba(15, 23, 42, 0.18)',
  '--rpv-page-radius': '8px',
} as CSSProperties;

export default function Demo() {
  return <PdfViewer source="/samples/mixed-sizes.pdf" layout="continuous" style={style} />;
}
```

See `--rpv-page-gap`, `--rpv-page-shadow`, `--rpv-page-border` and `--rpv-page-radius` in the [CSS variables reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/css-variables/) and the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

For layouts the viewer does not offer, such as two pages side by side, render pages yourself with [`usePdfDocument` and `PdfPageCanvas`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/).

## Limitations

- **No spreads.** Pages are always shown one per row. Workaround: a [headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) layout that places two `PdfPageCanvas` elements per row.
- **Scrolling fast shows blank pages briefly.** Pages outside the rendered window paint when they come into view. Workaround: none needed for most documents; very slow devices benefit from a lower `maxCanvasPixels`.
- **Page sizes are read when the document opens.** Every page's size is measured so the scrollbar is accurate, which takes a moment on documents with thousands of pages; pages render as usual meanwhile.
- **Search and links render per visible page.** Highlights and link areas appear as pages render, which is expected.

## API

- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Thumbnails

> Add a sidebar of page previews that shows where you are and jumps to any page.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/thumbnails/

## Basics

`thumbnails` adds a sidebar with a small preview of every page; the current page is highlighted.

*Example: Page thumbnails* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return <PdfViewer source="/samples/multipage.pdf" thumbnails />;
}
```

Thumbnails render lazily as they scroll into view, so a long document costs nothing until the user looks at it. Each thumbnail is a button labeled with its page number, and the sidebar is a named navigation region.

## Customization

The sidebar width and the highlight color are CSS variables; the sidebar's accessible name comes from `labels.thumbnails`.

*Example: A narrower sidebar with an orange highlight* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

const style = {
  '--rpv-thumbnails-width': '112px',
  '--rpv-accent': '#c2410c',
} as CSSProperties;

export default function Demo() {
  return (
    <PdfViewer
      source="/samples/multipage.pdf"
      thumbnails
      labels={{ thumbnails: 'Page overview' }}
      style={style}
    />
  );
}
```

See `--rpv-thumbnails-width` and `--rpv-accent` in the [CSS variables reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/css-variables/) and the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

For a thumbnail strip in your own layout (for example horizontally under the page), use [`PdfPageCanvas`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) with a small `scale` or `fit` box.

## Limitations

- **Hidden in compact layouts.** Below `compactBreakpoint` the sidebar is hidden outside fullscreen, because it would take too much of a narrow screen. Workaround: lower `compactBreakpoint`, or build a horizontal strip with [`PdfPageCanvas`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/).
- **Thumbnails stay rendered.** A thumbnail is kept once it has been shown, so memory grows with the number of pages scrolled past. Workaround: a [headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) strip that renders only what is on screen.
- **The sidebar is on the left.** Its position is fixed. Workaround: a [headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) layout.

## API

- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Loading states

> Show a loading view while a document downloads and an empty state when there is nothing to show, or replace both with your own.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/loading-states/

## Basics

While a document loads, the viewer shows a spinner and a status message, and keeps the toolbar in place.

*Example: The loading view* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

// A request that never finishes, so the loading view stays visible.
const neverResolves = () => new Promise<Response>(() => undefined);

export default function Demo() {
  return <PdfViewer source="/samples/letter-3pages.pdf" fetcher={neverResolves} />;
}
```

The message is announced to assistive technology, and zoom and rotation already work so the first page renders the way the user left it.

### Empty state

With no source (`null`, `undefined` or `''`) the viewer is idle: the toolbar shows `0 / 0` with navigation disabled and nothing loads.

*Example: No document yet* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // No source yet: the toolbar shows 0 / 0 with navigation disabled.
  return <PdfViewer source={null} />;
}
```

An empty source is idle from the very first render, so pages that pick a document later never flash a spinner.

## Document status

`status` on the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/) and on [`usePdfDocument`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) is one of `idle`, `loading`, `ready` or `error`. The root element also carries `data-status`, so you can style states in CSS:

```css
.report-viewer[data-status='loading'] {
  opacity: 0.8;
}
```

## Customization

`renderLoading` and `renderEmpty` replace the two views, for example with a skeleton that matches your page.

*Example: A skeleton loading view* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

const neverResolves = () => new Promise<Response>(() => undefined);

export default function Demo() {
  return (
    <PdfViewer
      source="/samples/letter-3pages.pdf"
      fetcher={neverResolves}
      renderLoading={() => (
        <div role="status" style={{ padding: 32, textAlign: 'center' }}>
          <div
            aria-hidden="true"
            style={{
              width: 240,
              height: 310,
              margin: '0 auto 12px',
              background: '#e2e8f0',
              borderRadius: 8,
            }}
          />
          Preparing your document…
        </div>
      )}
    />
  );
}
```

Label text comes from `labels.loading` and `labels.empty`; see the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

`onDocumentLoad` fires once the document is ready, with its page count; see [Events](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/events/).

## Limitations

- **No download progress.** The loading view does not show a percentage, because the viewer fetches the whole file before handing it to PDF.js. Workaround: download the file yourself with progress, then pass the bytes as the `source`.
- **Page rendering has no separate indicator.** After the document loads, each page appears when it finishes rendering; the previous page stays visible until then, so there is no blank flash.

## API

- [PdfDocumentStatus](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-document-status/)
- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Keyboard shortcuts

> Navigate, zoom, rotate and go fullscreen from the keyboard while focus is in the viewer, without stealing keys from the rest of your app.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/keyboard-shortcuts/

## Basics

Shortcuts work while focus is anywhere inside the viewer; click the page or <kbd>Tab</kbd> to the document area first.

*Example: Try the shortcuts* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // Click the page (or Tab to it), then try ← →, + − 0, r and f.
  return <PdfViewer source="/samples/multipage.pdf" />;
}
```

Shortcuts never fire while typing in an input, select or text area, or with <kbd>Alt</kbd>, <kbd>Ctrl</kbd> or <kbd>⌘</kbd> held, so browser and app shortcuts keep working. <kbd>Shift</kbd> with arrows is left alone for text selection.

### Toolbar and menu keys

The toolbar is a single tab stop. Inside it, <kbd>←</kbd> <kbd>→</kbd> <kbd>Home</kbd> <kbd>End</kbd> move between controls. On the **⋮ More actions** button, <kbd>Enter</kbd>, <kbd>Space</kbd> or <kbd>↓</kbd> open the menu on its first item and <kbd>↑</kbd> on its last; inside the menu, arrows move and <kbd>Esc</kbd> closes it and returns focus.

### Turning shortcuts off

`keyboardShortcuts={false}` disables the viewer's shortcuts when your app uses the same keys. Toolbar, menu and form keys keep working.

*Example: Shortcuts disabled* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // Your app owns these keys: the viewer ignores them. The toolbar stays keyboard-operable.
  return <PdfViewer source="/samples/multipage.pdf" keyboardShortcuts={false} />;
}
```

## Customization

Focus indicators are CSS variables: `--rpv-focus-ring` and `--rpv-focus-ring-offset` for toolbar controls, `--rpv-accent` for the document area, thumbnails and links.

*Example: A high-visibility focus ring* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

// A stronger focus ring for the toolbar and a matching accent for the document area.
const style = {
  '--rpv-focus-ring': '3px solid #facc15',
  '--rpv-focus-ring-offset': '3px',
  '--rpv-accent': '#ca8a04',
} as CSSProperties;

export default function Demo() {
  return <PdfViewer source="/samples/multipage.pdf" style={style} />;
}
```

See the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/) and [Accessibility](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/accessibility/).

## Advanced / escape hatch

To bind different keys, turn the built-in shortcuts off and call the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/) from your own key handler:

```tsx
const viewer = useRef<PdfViewerApi>(null);

useEffect(() => {
  const onKey = (event: KeyboardEvent) => {
    if (event.key === 'j') viewer.current?.nextPage();
    if (event.key === 'k') viewer.current?.previousPage();
  };
  window.addEventListener('keydown', onKey);
  return () => window.removeEventListener('keydown', onKey);
}, []);

<PdfViewer ref={viewer} source={url} keyboardShortcuts={false} />;
```

## Limitations

- **Keys cannot be remapped.** The key map is fixed. Workaround: disable it and handle keys yourself, as above.
- **Shortcuts need focus inside the viewer.** They are scoped to the viewer so they never interfere with the rest of the page. Workaround: focus the viewer programmatically, or use a global handler with the imperative API.

## API

- [Keyboard shortcuts](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/keyboard-shortcuts/)

---

# Page number input

> Let users type the page they want in the toolbar instead of clicking through a long document.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/page-number-input/

## Basics

`pageInput` turns the page label into an editable field followed by the page count.

*Example: Type a page number* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return <PdfViewer source="/samples/multipage.pdf" pageInput />;
}
```

<kbd>Enter</kbd> or leaving the field goes to the page; <kbd>Esc</kbd> reverts to the current page.
Numbers outside the document are clamped to the first or last page. The field is sized to the number
of digits in the page count, and on narrow toolbars it stays visible while other actions move into
the **More actions** menu.

## Customization

The field's background and border are CSS variables, and its accessible name is `labels.pageInput`.

*Example: A light toolbar with a white page field* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

const style = {
  '--rpv-toolbar-bg': '#f1f5f9',
  '--rpv-toolbar-fg': '#0f172a',
  '--rpv-button-hover-bg': 'rgba(15, 23, 42, 0.08)',
  '--rpv-button-disabled-fg': 'rgba(15, 23, 42, 0.35)',
  '--rpv-focus-ring': '2px solid #0f172a',
  '--rpv-page-input-bg': '#ffffff',
  '--rpv-page-input-border': '#94a3b8',
} as CSSProperties;

export default function Demo() {
  return (
    <PdfViewer
      source="/samples/multipage.pdf"
      pageInput
      labels={{ pageInput: 'Go to page' }}
      style={style}
    />
  );
}
```

See `--rpv-page-input-bg` and `--rpv-page-input-border` in the [CSS variables reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/css-variables/) and the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

[`PdfToolbar`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar/) accepts a `pageIndicator` node, so a custom toolbar can put any control, such as a page select, in the same place.

## Limitations

- **Numeric pages only.** Page labels defined inside the PDF (such as “iv” or “A-1”) are not shown or accepted. Workaround: map your own labels to page numbers and drive `page` from a custom control.

## API

- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Links

> Make links inside documents work, jumping to pages within the document or opening websites safely in a new tab.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/links/

## Basics

`annotationLayer` makes link annotations clickable: internal links go to their page and external links open in a new tab.

*Example: Internal and external links* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // Internal links jump to their page; external links open in a new tab.
  return <PdfViewer source="/samples/links.pdf" annotationLayer />;
}
```

External links open with `rel="noopener noreferrer nofollow"`, so the opened site cannot reach back into your page. Link areas are keyboard-focusable and show a focus outline.

## Customization

The focus outline of link areas uses `--rpv-accent`. Combining links with the [text layer](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/text-layer/) keeps text selectable around them.

*Example: Links with selectable text and a pink focus color* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

// --rpv-accent colors the keyboard focus outline of link areas.
const style = { '--rpv-accent': '#be185d' } as CSSProperties;

export default function Demo() {
  return <PdfViewer source="/samples/links.pdf" annotationLayer textLayer style={style} />;
}
```

See the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

[`PdfPageCanvas`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) accepts `annotationLayer` and an `onLinkNavigate(page)` callback, so custom layouts decide what an internal link does.

## Limitations

- **Only links are interactive.** Form fields, comments and other annotations are not rendered as interactive elements. Workaround: none within the viewer; fill forms in a dedicated PDF form tool.
- **External links always open a new tab.** Workaround: none within the viewer. If you need to intercept external navigation, render pages with [`PdfPageCanvas`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) and handle clicks on the link layer yourself.

## API

- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Text layer

> Overlay real, selectable text on each page so users can copy it and screen readers can read the document.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/text-layer/

## Basics

`textLayer` places invisible, selectable text over each rendered page.

*Example: Select and copy text* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // Select text on the page and copy it.
  return <PdfViewer source="/samples/multipage.pdf" textLayer />;
}
```

With a text layer, each page is exposed to assistive technology as a group containing its text instead of a single image, so screen readers can read the document. Browser find (<kbd>Ctrl</kbd>/<kbd>⌘</kbd> + <kbd>F</kbd> outside the viewer) finds text on rendered pages too.

[Search](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/search/) turns the text layer on automatically.

## Customization

The selection color is `--rpv-selection-bg`.

*Example: A green selection on a document with standard fonts* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

const style = { '--rpv-selection-bg': 'rgba(16, 185, 129, 0.35)' } as CSSProperties;

export default function Demo() {
  return <PdfViewer source="/samples/non-embedded-fonts.pdf" textLayer style={style} />;
}
```

See the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

[`PdfPageCanvas`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) accepts `textLayer` for custom layouts, and `getDocument()` on the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/) gives access to PDF.js `getTextContent()` for extracting text without rendering.

## Limitations

- **Scanned documents have no text.** Pages that are images contain no text to select. Workaround: run OCR on the server and serve a PDF with a text layer.
- **Selection can be imprecise with unusual fonts.** Text is positioned from the PDF's font metrics, so some fonts select slightly off the visible glyphs. Workaround: none within the viewer.
- **Adds work per page.** Building the text layer costs extra time on text-heavy pages. Workaround: leave it off when users only need to look at the document.

## API

- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Search

> Find text across the whole document, highlight every match, and step through results from the keyboard.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/search/

## Basics

`search` adds a find bar that searches every page and highlights the matches.

*Example: Search a 40-page document* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // Type in the search field (or press Ctrl/⌘ + F inside the viewer); Enter steps through matches.
  return <PdfViewer source="/samples/multipage.pdf" search />;
}
```

Matching is case-insensitive. <kbd>Enter</kbd> and <kbd>Shift</kbd> + <kbd>Enter</kbd> step to the next and previous match, moving to its page and scrolling it into view; <kbd>Esc</kbd> clears the field. With focus inside the viewer, <kbd>Ctrl</kbd>/<kbd>⌘</kbd> + <kbd>F</kbd> focuses the search field. The result count (“2 of 5”) is announced as it changes. `search` implies the [text layer](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/text-layer/).

### With continuous layout

In [continuous layout](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/continuous-layout/), stepping through matches scrolls the document to each one.

*Example: Search with continuous layout and thumbnails* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return <PdfViewer source="/samples/multipage.pdf" search layout="continuous" thumbnails />;
}
```

## Customization

Highlight colors and the search bar surface are CSS variables; the field's name and result texts are labels.

*Example: Blue highlights and shorter result labels* — the source of the live demo on this page.

```tsx
import { PdfViewer, type PdfViewerLabels } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

const labels: Partial<PdfViewerLabels> = {
  search: 'Find in report',
  searchResults: (current, total) => `${current}/${total}`,
  searchNoResults: 'Nothing found',
};

const style = {
  '--rpv-search-highlight': 'rgba(56, 189, 248, 0.45)',
  '--rpv-search-highlight-selected': 'rgba(37, 99, 235, 0.6)',
  '--rpv-search-bg': '#f8fafc',
} as CSSProperties;

export default function Demo() {
  return <PdfViewer source="/samples/multipage.pdf" search labels={labels} style={style} />;
}
```

See `--rpv-search-highlight`, `--rpv-search-highlight-selected`, `--rpv-search-bg` and `--rpv-search-fg` in the [CSS variables reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/css-variables/) and the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

[`PdfPageCanvas`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) accepts a `highlight` (`{ query, selected }`, see [`PageHighlight`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/page-highlight/)) and reports the selected match's element through `onHighlight`, so a custom UI can drive search itself.

## Limitations

- **Plain text matching.** No regular expressions, whole-word or case-sensitive options. Workaround: extract text with `getDocument()` and PDF.js `getTextContent()`, search it yourself, and highlight with `PdfPageCanvas`.
- **Text split across lines is not matched as one phrase.** A phrase broken by a line or column break may not be found. Workaround: search for a shorter part of the phrase.
- **Scanned pages cannot be searched.** Image-only pages contain no text; see [Text layer](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/text-layer/).

## API

- [PageHighlight](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/page-highlight/)
- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Password protection

> Open encrypted documents with a password your app provides, or ask the user for it with a built-in or custom form.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/password-protection/

## Basics

`passwordPrompt` shows a password form when a document is encrypted, and asks again after a wrong password.

*Example: Ask for the password (it is “demo”)* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // The password for this sample is: demo
  return <PdfViewer source="/samples/password.pdf" passwordPrompt />;
}
```

The form is labeled, announces an incorrect password as an alert, and marks the field invalid. Without `passwordPrompt`, an encrypted document reports a `PASSWORD_REQUIRED` [error](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/error-states/).

### A known password

Pass `password` when your app already has it; changing it reloads the document.

*Example: Open with a known password* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // A password your app already knows, for example from the server.
  return <PdfViewer source="/samples/password.pdf" password="demo" />;
}
```

## Customization

`renderPasswordPrompt({ incorrect, submit })` replaces the form with your own.

*Example: A custom unlock form* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import { useId, useState } from 'react';

function UnlockForm({
  incorrect,
  submit,
}: {
  incorrect: boolean;
  submit: (password: string) => void;
}) {
  const id = useId();
  const [value, setValue] = useState('');

  return (
    <form
      style={{ display: 'grid', gap: 8, maxWidth: 320, margin: '48px auto', textAlign: 'center' }}
      onSubmit={(event) => {
        event.preventDefault();
        submit(value);
      }}
    >
      <strong>🔒 This file is protected</strong>
      <label htmlFor={id}>Enter the document password (hint: demo)</label>
      <input
        id={id}
        type="password"
        value={value}
        aria-invalid={incorrect}
        onChange={(event) => setValue(event.target.value)}
      />
      {incorrect && <span role="alert">That password didn’t work.</span>}
      <button type="submit">Unlock</button>
    </form>
  );
}

export default function Demo() {
  return (
    <PdfViewer
      source="/samples/password.pdf"
      passwordPrompt
      renderPasswordPrompt={(context) => <UnlockForm {...context} />}
    />
  );
}
```

Default texts are `labels.passwordPrompt`, `passwordLabel`, `passwordSubmit` and `passwordIncorrect`; see [Labels and locale](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/labels/) and the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

With [`usePdfDocument`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/), pass `password` in the options and handle `error.code === 'PASSWORD_REQUIRED'` or `'INCORRECT_PASSWORD'` in your own UI.

## Limitations

- **The password is held in memory.** Anything that controls `password` (props, state) can read it. Workaround: clear it from your state after the document opens if that matters in your threat model.
- **Owner restrictions are not enforced.** Permissions such as “no printing” or “no copying” set by the PDF author are not applied by the viewer. Workaround: hide `print` and `download` from the toolbar and leave `textLayer` off for such documents.

## API

- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Labels and locale

> Translate or reword every visible text and accessible name, and format numbers for any locale.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/labels/

## Basics

`labels` overrides any subset of the viewer's texts and `locale` sets number formatting.

*Example: Hungarian labels* — the source of the live demo on this page.

```tsx
import { PdfViewer, type PdfViewerLabels } from '@kingtech/react-pdf-viewer';

// Defined once at module scope: labels are merged per object identity.
const hungarian: Partial<PdfViewerLabels> = {
  viewer: 'PDF-megjelenítő',
  toolbar: 'PDF-vezérlők',
  zoomIn: 'Nagyítás',
  zoomOut: 'Kicsinyítés',
  previousPage: 'Előző oldal',
  nextPage: 'Következő oldal',
  enterFullscreen: 'Teljes képernyő',
  exitFullscreen: 'Kilépés a teljes képernyőből',
  rotate: 'Forgatás',
  download: 'Letöltés',
  print: 'Nyomtatás',
  moreActions: 'További műveletek',
  pageAriaLabel: (page, total, { formatNumber }) =>
    `${formatNumber(page)}. oldal, összesen ${formatNumber(total)}`,
};

export default function Demo() {
  return <PdfViewer source="/samples/multipage.pdf" locale="hu-HU" labels={hungarian} />;
}
```

Keys you leave out keep their English defaults, exported as `defaultLabels`. Accessible names (the toolbar, buttons, the page image, the thumbnails sidebar) are labels too, so a translated viewer is fully translated for screen reader users.

> **Warning: Keep the labels object stable**
>
> Labels are merged once per object identity. An object literal written inline in JSX creates a new
>   object on every render. Define labels at module scope, or wrap them in `useMemo`.

### Text and function labels

Most labels are strings. Labels that contain numbers are functions that receive the numbers and a `{ formatNumber }` helper, so they read naturally in any language:

```tsx
const labels = {
  pageIndicator: (page, total, { formatNumber }) =>
    `${formatNumber(page)} / ${formatNumber(total)}`,
  printProgress: (done, total) => `Preparing ${done} of ${total}…`,
};
```

Every key, its default and its purpose is listed in the [`PdfViewerLabels` reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-labels/).

## Customization

`formatNumber` follows `locale`, including non-Latin digits.

*Example: Persian digits with custom page and zoom labels* — the source of the live demo on this page.

```tsx
import { PdfViewer, type PdfViewerLabels } from '@kingtech/react-pdf-viewer';

// Label functions receive a formatter for the viewer's locale.
const labels: Partial<PdfViewerLabels> = {
  zoomLevel: (percent, { formatNumber }) => `${formatNumber(percent)} %`,
  pageIndicator: (page, total, { formatNumber }) =>
    `${formatNumber(page)} از ${formatNumber(total)}`,
};

export default function Demo() {
  // Arabic-Indic digits from the "fa-IR" locale.
  return <PdfViewer source="/samples/multipage.pdf" locale="fa-IR" labels={labels} />;
}
```

For translation workflows and right-to-left pages, see the [Localization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/localization/) and the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

`PdfToolbar` accepts the same `labels` and `locale` props for [custom toolbars](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar/).

## Limitations

- **Error messages are not labels.** `PdfViewerError.message` is English. Workaround: map `error.code` to translated text in [`renderError`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/error-states/).
- **No bundled translations.** Only English ships with the package. Workaround: keep a labels object per language in your app's translation files.
- **No right-to-left toolbar mirroring.** The toolbar keeps its order in right-to-left pages. Workaround: build a mirrored toolbar with [`renderToolbar`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar/).

## API

- [PdfViewerLabels](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-labels/)
- [defaultLabels](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/default-labels/)
- [LabelContext](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/label-context/)

---

# PDF.js configuration

> Self-host the PDF.js worker, fonts and decoders for production, choose a build for older browsers, and tune PDF.js per viewer.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/

PDF.js runs its parser in a Web Worker and loads extra assets on demand: CMaps for CJK text, standard font data for documents that don't embed their fonts, WebAssembly image decoders and ICC color profiles. The viewer loads PDF.js lazily, the first time a document opens.

## Basics

Call `configurePdfJs` once at startup to self-host the worker and assets.

*Example: A document that needs standard font data* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

/*
 * This site calls configurePdfJs once at startup with a self-hosted worker and assets:
 *
 *   configurePdfJs({
 *     workerSrc,
 *     cMapUrl: '/pdfjs/cmaps/',
 *     standardFontDataUrl: '/pdfjs/standard_fonts/',
 *     wasmUrl: '/pdfjs/wasm/',
 *     iccUrl: '/pdfjs/iccs/',
 *   });
 *
 * This document uses the standard 14 fonts without embedding them, so it needs the font data.
 */
export default function Demo() {
  return <PdfViewer source="/samples/non-embedded-fonts.pdf" />;
}
```

```ts
import { configurePdfJs } from '@kingtech/react-pdf-viewer';
import workerSrc from 'pdfjs-dist/build/pdf.worker.min.mjs?url';

configurePdfJs({
  workerSrc,
  cMapUrl: '/pdfjs/cmaps/',
  standardFontDataUrl: '/pdfjs/standard_fonts/',
  wasmUrl: '/pdfjs/wasm/',
  iccUrl: '/pdfjs/iccs/',
});
```

Without configuration everything still works: the worker and assets load from jsDelivr, pinned to the **exact installed** `pdfjs-dist` version, and a one-time notice in development reminds you to self-host. Copy the asset folders from `node_modules/pdfjs-dist/` as part of your build; the [Vite](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/vite/), [Next.js](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/nextjs/) and [webpack](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/webpack/) pages show how.

### Older browsers

PDF.js 6 relies on recent browser APIs. Load the legacy build for older browsers:

```ts
configurePdfJs({
  loader: () => import('pdfjs-dist/legacy/build/pdf.mjs'),
  workerSrc: legacyWorkerUrl, // from pdfjs-dist/legacy/build/pdf.worker.min.mjs
});
```

### Your own worker

Pass a ready `Worker` as `workerPort` when you create the worker yourself, for example to share it or to satisfy a strict Content Security Policy. A worker you have already set on PDF.js's `GlobalWorkerOptions` is respected too.

## Content Security Policy

- `worker-src` must allow the worker's origin, or `blob:` when you pass a `workerPort` created from a blob.
- `connect-src` must allow the asset URLs (and the document URLs).
- `script-src` needs no `'unsafe-eval'`: the viewer passes `isEvalSupported: false`. Documents with JPEG 2000 or JBIG2 images use the WebAssembly decoders, which a strict policy allows with `'wasm-unsafe-eval'`.

## Customization

`pdfjsOptions` passes per-viewer options to PDF.js `getDocument`, for example its log level.

*Example: Per-viewer PDF.js options* — the source of the live demo on this page.

```tsx
import { PdfViewer, type PdfJsDocumentOptions } from '@kingtech/react-pdf-viewer';

// Per-instance getDocument options: silence PDF.js console warnings for this viewer only.
const pdfjsOptions: PdfJsDocumentOptions = { verbosity: 0 };

export default function Demo() {
  return <PdfViewer source="/samples/mixed-sizes.pdf" pdfjsOptions={pdfjsOptions} />;
}
```

Source-related options (URL, data, password, headers) are managed by the viewer and cannot be overridden this way. See the [`PdfJsDocumentOptions` reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-js-document-options/) and the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

`loader` can return any `pdfjs-dist` module instance, for example one your app already imported. `getDocument()` on the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/) exposes the loaded PDF.js document for anything the viewer does not wrap.

## Limitations

- **Configuration is global.** `configurePdfJs` applies to every viewer on the page. Workaround: use `pdfjsOptions` for per-viewer differences.
- **Configure before the first document opens.** Changing `loader` later only affects documents opened afterwards; a worker change applies to the loaded module. Workaround: call it in your entry module.
- **The CDN default is for development.** Loading the worker from a third-party CDN depends on its availability and your CSP. Workaround: self-host as shown above.
- **Only the tested `pdfjs-dist` 6 line is supported.** The peer range is `^6.0.0`; the test suite runs against 6.3. See [Requirements](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/requirements/).

## API

- [configurePdfJs](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/configure-pdf-js/)
- [PdfJsConfig](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-js-config/)
- [PdfJsDocumentOptions](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-js-document-options/)
- [PdfJsModule](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-js-module/)

---

# Authenticated requests

> Load protected PDFs with cookies, headers or your own HTTP client, and handle expired sessions and HTTP errors your way.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/authenticated-requests/

URL sources are fetched by the viewer with `fetch` and handed to PDF.js as bytes. That lets you shape the request, and lets the viewer see HTTP statuses.

## Basics

`requestInit` adds `fetch` options such as credentials and headers to the request.

*Example: Send cookies and a header* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // Extra fetch options for URL sources: send cookies and a custom header.
  return (
    <PdfViewer
      source="/samples/letter-3pages.pdf"
      requestInit={{ credentials: 'include', headers: { 'X-Requested-With': 'react-pdf-viewer' } }}
    />
  );
}
```

### Your own HTTP client

`fetcher(url, init)` replaces `fetch` entirely, for example with a client that adds and refreshes tokens. `init.signal` is aborted when the source changes.

*Example: A bearer token and a handled 401* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import { useCallback, useState } from 'react';

const TOKEN = 'demo-token';

/** Stand-in for your API: rejects requests without a bearer token. */
async function api(url: string, init: RequestInit): Promise<Response> {
  if (new Headers(init.headers).get('Authorization') !== `Bearer ${TOKEN}`) {
    return new Response(JSON.stringify({ title: 'Unauthorized', status: 401 }), {
      status: 401,
      headers: { 'Content-Type': 'application/problem+json' },
    });
  }
  return fetch(url, { signal: init.signal ?? null });
}

export default function Demo() {
  const [signedIn, setSignedIn] = useState(false);
  const [notice, setNotice] = useState('');

  const fetcher = useCallback(
    (url: string, init: RequestInit) =>
      api(url, { ...init, headers: signedIn ? { Authorization: `Bearer ${TOKEN}` } : {} }),
    [signedIn],
  );

  return (
    <>
      <p className="demo-controls">
        <label>
          <input
            type="checkbox"
            checked={signedIn}
            onChange={(event) => {
              setSignedIn(event.target.checked);
              setNotice('');
            }}
          />
          Signed in
        </label>
        {notice && <span role="status">{notice}</span>}
      </p>
      <PdfViewer
        // The fetcher applies to the next load: remount to load again with the new token.
        key={String(signedIn)}
        source="/samples/letter-3pages.pdf"
        fetcher={fetcher}
        onHttpError={(response) => {
          if (response.status === 401) {
            setNotice('401 Unauthorized: sign in to load the document.');
            return true; // handled: empty state, no error view, no onError
          }
          return false;
        }}
      />
    </>
  );
}
```

### Handling HTTP errors

`onHttpError(response)` runs for every non-2xx response. Return `true` when you have handled it (for example by redirecting to sign-in): the viewer shows its empty state and does not call `onError`. Otherwise the error view appears with the message from `getHttpErrorMessage`.

```tsx
<PdfViewer
  source={url}
  requestInit={{ credentials: 'include' }}
  onHttpError={(response) => {
    if (response.status === 401) {
      signIn({ returnTo: window.location.href });
      return true;
    }
  }}
/>
```

By default the message is read from an RFC 9457 (formerly 7807) problem body (`detail`, then `title`), then a JSON string body, then `Failed to fetch PDF: {status} {statusText}`.

## Cross-origin documents

Reading a PDF from another origin requires the server to send `Access-Control-Allow-Origin` for your origin; sending cookies additionally requires `credentials: 'include'` and `Access-Control-Allow-Credentials: true`. A blocked request surfaces as a `NETWORK_ERROR`, because browsers do not tell scripts why a request was blocked. When you cannot change the server, proxy the file through your own origin.

## Customization

`getHttpErrorMessage(response)` writes the error message for your API's statuses. Return `undefined` to keep the default.

*Example: A friendlier 404 message* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return (
    <PdfViewer
      source="/samples/archived-report.pdf"
      getHttpErrorMessage={
        async (response) =>
          response.status === 404
            ? 'This report has been archived. Ask your administrator to restore it.'
            : undefined // use the default message for other statuses
      }
    />
  );
}
```

To replace the whole error view, see [Error states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/error-states/) and the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

If requests need logic the viewer cannot express (multi-step downloads, signed URLs fetched from another endpoint), fetch the bytes yourself and pass them as a `Blob` or `ArrayBuffer` [source](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/document-sources/). [`usePdfDocument`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) accepts the same request options.

## Limitations

- **Request options apply to the next load.** Changing `requestInit` or `fetcher` does not reload the current document. Workaround: change the viewer's `key`, or call `reload()` on the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/).
- **No range requests.** The whole file is downloaded before rendering starts. Workaround: none within the viewer; keep very large documents split server-side.
- **Error handlers share one response.** `onHttpError` and `getHttpErrorMessage` receive the same `Response`, and the default message reads a clone of its body. Workaround: read bodies with `response.clone().json()` in your handlers so the default message still works.

## API

- [PdfRequestOptions](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-request-options/)

---

# Controlled state

> Own the page, zoom, rotation and fullscreen state in your app, or let the viewer keep it, with one consistent pattern.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/controlled-state/

Every piece of viewer state follows the same trio: `x` controls the value, `defaultX` sets its initial value while uncontrolled, and `onXChange` reports every change in both modes.

| State      | Controlled   | Initial             | Change               |
| ---------- | ------------ | ------------------- | -------------------- |
| Page       | `page`       | `defaultPage`       | `onPageChange`       |
| Zoom       | `scale`      | `defaultScale`      | `onScaleChange`      |
| Rotation   | `rotation`   | `defaultRotation`   | `onRotationChange`   |
| Fullscreen | `fullscreen` | `defaultFullscreen` | `onFullscreenChange` |

## Basics

Pass a value and its change handler to put that state in your app.

*Example: Page, zoom and rotation controlled from outside* — the source of the live demo on this page.

```tsx
import { PdfViewer, type Rotation } from '@kingtech/react-pdf-viewer';
import { useState } from 'react';

export default function Demo() {
  const [page, setPage] = useState(1);
  const [scale, setScale] = useState(1);
  const [rotation, setRotation] = useState<Rotation>(0);

  return (
    <>
      <p className="demo-controls">
        <button type="button" className="demo-button" onClick={() => setPage(99)}>
          Go to page 99
        </button>
        <button
          type="button"
          className="demo-button"
          onClick={() => setScale((s) => Math.min(5, s + 0.25))}
        >
          Zoom +25%
        </button>
        <button
          type="button"
          className="demo-button"
          onClick={() => {
            setPage(1);
            setScale(1);
            setRotation(0);
          }}
        >
          Reset
        </button>
        <output>
          page {page} · {Math.round(scale * 100)}% · {rotation}°
        </output>
      </p>
      <PdfViewer
        source="/samples/multipage.pdf"
        page={page}
        onPageChange={setPage}
        scale={scale}
        onScaleChange={setScale}
        rotation={rotation}
        onRotationChange={setRotation}
      />
    </>
  );
}
```

The toolbar and keyboard keep working: they call your handlers, and the viewer shows whatever you pass back. Out-of-range values are clamped and reported: “Go to page 99” shows page 40 and calls `onPageChange(40)`.

### Mixing modes

Control only what you need. Leave `x` undefined to let the viewer keep that state, and use `defaultX` to choose where it starts; the demo under [Customization](#customization) opens on page 5 at 125%, rotated.

Switching a prop between controlled and uncontrolled during the component's life logs a development warning, as React inputs do.

## The same pattern in your components

`useControllableState` is the hook the viewer uses. Build your own controls with the same contract.

*Example: useControllableState in a custom component* — the source of the live demo on this page.

```tsx
import { useControllableState } from '@kingtech/react-pdf-viewer';
import { useState } from 'react';

interface CounterProps {
  value?: number;
  defaultValue?: number;
  onValueChange?: (value: number) => void;
}

/** A component with the same value / defaultValue / onValueChange contract as the viewer. */
function Counter({ value, defaultValue = 0, onValueChange }: CounterProps) {
  const [count, setCount] = useControllableState({
    value,
    defaultValue,
    onChange: onValueChange,
    name: 'value',
  });
  return (
    <button
      type="button"
      className="demo-button"
      onClick={() => setCount((previous) => previous + 1)}
    >
      Clicked {count} times
    </button>
  );
}

export default function Demo() {
  const [controlled, setControlled] = useState(10);

  return (
    <div className="demo-controls">
      <span>Uncontrolled:</span>
      <Counter defaultValue={0} />
      <span>Controlled (capped at 12):</span>
      <Counter value={controlled} onValueChange={(next) => setControlled(Math.min(12, next))} />
    </div>
  );
}
```

The setter is stable and accepts a value or an updater function; `onChange` fires only when the value actually changes.

## Customization

Start an uncontrolled viewer exactly where you want it. The zoom reset button returns to `defaultScale`.

*Example: Uncontrolled, with chosen starting values* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // Uncontrolled, but opening where you choose. `defaultScale` is also the reset-zoom target.
  return (
    <PdfViewer
      source="/samples/multipage.pdf"
      defaultPage={5}
      defaultScale={1.25}
      defaultRotation={90}
      zoomReset
    />
  );
}
```

For styling, see the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

For one-off commands (“go to page 3”) without owning the state, use the [imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/).

## Limitations

- **Values cannot be functions.** `useControllableState` treats a function passed to the setter as an updater. Workaround: wrap function values in an object.
- **New documents reset uncontrolled state only.** A new `source` opens on `defaultPage`; a controlled `page` is yours to reset. Workaround: reset your state when you change the source.

## API

- [useControllableState](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-controllable-state/)
- [UseControllableStateOptions](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-controllable-state-options/)
- [SetControllableState](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/set-controllable-state/)

---

# Imperative API

> Command the viewer from your own buttons and code through a ref, and read its current page, zoom and status.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/

## Basics

A `ref` on `PdfViewer` gives you methods to navigate, zoom, rotate and go fullscreen.

*Example: Buttons outside the viewer* — the source of the live demo on this page.

```tsx
import { PdfViewer, type PdfViewerApi } from '@kingtech/react-pdf-viewer';
import { useRef } from 'react';

export default function Demo() {
  const viewer = useRef<PdfViewerApi>(null);

  return (
    <>
      <p className="demo-controls">
        <button type="button" className="demo-button" onClick={() => viewer.current?.goToPage(20)}>
          Page 20
        </button>
        <button type="button" className="demo-button" onClick={() => viewer.current?.setScale(2)}>
          Zoom to 200%
        </button>
        <button type="button" className="demo-button" onClick={() => viewer.current?.rotate('ccw')}>
          Rotate left
        </button>
        <button
          type="button"
          className="demo-button"
          onClick={() => viewer.current?.toggleFullscreen()}
        >
          Fullscreen
        </button>
      </p>
      <PdfViewer ref={viewer} source="/samples/multipage.pdf" />
    </>
  );
}
```

```tsx
const viewer = useRef<PdfViewerApi>(null);

<PdfViewer ref={viewer} source={url} />;

viewer.current?.goToPage(3);
```

| Read                                                                                       | Do                                                                                                                                                               |
| ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`, `numPages`, `page`, `scale`, `rotation`, `fullscreen`, `canZoomIn`, `canZoomOut` | `goToPage`, `nextPage`, `previousPage`, `zoomIn`, `zoomOut`, `setScale`, `resetZoom`, `rotate`, `toggleFullscreen`, `download`, `print`, `reload`, `getDocument` |

The same object is passed to [`renderToolbar`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar/). Its values are a snapshot of the render that produced it: read them from `ref.current` when you need them, rather than keeping an old copy. Every member is documented in the [`PdfViewerApi` reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-api/).

## Customization

Hide the toolbar and build the whole interface from the API: download, print, reload, or read document metadata through PDF.js.

*Example: A toolbar-less viewer driven by your buttons* — the source of the live demo on this page.

```tsx
import { PdfViewer, type PdfViewerApi } from '@kingtech/react-pdf-viewer';
import { useRef, useState } from 'react';

export default function Demo() {
  const viewer = useRef<PdfViewerApi>(null);
  const [info, setInfo] = useState('');

  return (
    <>
      <p className="demo-controls">
        <button
          type="button"
          className="demo-button"
          onClick={() => void viewer.current?.download()}
        >
          Download
        </button>
        <button type="button" className="demo-button" onClick={() => void viewer.current?.print()}>
          Print
        </button>
        <button type="button" className="demo-button" onClick={() => viewer.current?.reload()}>
          Reload
        </button>
        <button
          type="button"
          className="demo-button"
          onClick={async () => {
            const document = viewer.current?.getDocument();
            const metadata = await document?.getMetadata();
            setInfo(JSON.stringify(metadata?.info ?? {}, null, 2));
          }}
        >
          Read metadata
        </button>
      </p>
      {/* Toolbar hidden: the buttons above are the whole interface. */}
      <PdfViewer ref={viewer} source="/samples/letter-3pages.pdf" toolbar={false} />
      {info && <pre className="demo-output">{info}</pre>}
    </>
  );
}
```

See the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

`getDocument()` returns the loaded PDF.js `PDFDocumentProxy` (or `null`) for anything the viewer does not wrap: metadata, the outline, attachments or text extraction. Do not destroy it; the viewer owns its lifecycle.

## Limitations

- **Values are a snapshot.** An API object captured in an old closure reports old values. Workaround: read from `ref.current` at the time you need the value, or use the `onXChange` [events](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/events/).
- **`ref.current` is `null` before mount.** Workaround: guard with optional chaining, as in the examples.
- **The PDF.js document is not a stable API of this package.** Its methods follow `pdfjs-dist`. Workaround: pin `pdfjs-dist` when you depend on them.

## API

- [PdfViewerApi](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-api/)

---

# Events

> React to the document loading, pages rendering and the user navigating, for analytics, logging or your own UI.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/events/

## Basics

Pass callbacks to hear what the viewer is doing.

*Example: An event log* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import { useState } from 'react';

export default function Demo() {
  const [log, setLog] = useState<string[]>([]);
  const record = (line: string) => setLog((previous) => [line, ...previous].slice(0, 8));

  return (
    <>
      <PdfViewer
        source="/samples/multipage.pdf"
        onDocumentLoad={({ numPages, fingerprint }) =>
          record(`onDocumentLoad: ${numPages} pages, ${fingerprint.slice(0, 8)}…`)
        }
        onPageRender={({ page, scale, durationMs }) =>
          record(
            `onPageRender: page ${page} at ${Math.round(scale * 100)}% in ${Math.round(durationMs)} ms`,
          )
        }
        onPageChange={(page) => record(`onPageChange: ${page}`)}
        onScaleChange={(scale) => record(`onScaleChange: ${scale}`)}
      />
      <pre className="demo-output" aria-label="Event log">
        {log.length ? log.join('\n') : 'Waiting for events…'}
      </pre>
    </>
  );
}
```

| Callback                                                                  | Called with                                  | When                                                                                          |
| ------------------------------------------------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `onDocumentLoad`                                                          | `{ numPages, fingerprint }`                  | A document finished loading                                                                   |
| `onPageRender`                                                            | `{ page, scale, width, height, durationMs }` | A page finished rendering                                                                     |
| `onPageChange`, `onScaleChange`, `onRotationChange`, `onFullscreenChange` | the new value                                | The user or your code changed it; see [Controlled state](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/controlled-state/) |
| `onError`                                                                 | `PdfViewerError`                             | Something failed; see [Error states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/error-states/)                         |
| `onDownload`, `onPrint`                                                   | the file, or nothing                         | Before saving or printing; return `false` to take over                                        |
| `onHttpError`                                                             | `Response`                                   | A URL source returned a non-2xx status                                                        |

`fingerprint` identifies the document's content, which is handy as a cache or analytics key. Callbacks can change between renders without reloading anything.

## Customization

Use render measurements to drive your own UI, such as a status bar.

*Example: Show the size and render time of each page* — the source of the live demo on this page.

```tsx
import { PdfViewer, type PageRenderInfo } from '@kingtech/react-pdf-viewer';
import { useState } from 'react';

export default function Demo() {
  const [last, setLast] = useState<PageRenderInfo | null>(null);

  return (
    <>
      <p className="demo-output" role="status">
        {last
          ? `Page ${last.page}: ${Math.round(last.width)} × ${Math.round(last.height)} px, rendered in ${Math.round(last.durationMs)} ms`
          : 'Render a page to measure it.'}
      </p>
      <PdfViewer source="/samples/mixed-sizes.pdf" onPageRender={setLast} zoomReset />
    </>
  );
}
```

See the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

`PdfPageCanvas` has its own `onRender` and `onError` for [headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) layouts.

## Limitations

- **No scroll or selection events.** The viewer does not report scroll position or text selection. Workaround: listen on the document area yourself (`.rpv-viewport`) or use `document.getSelection()` with the [text layer](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/text-layer/).
- **`onPageRender` fires often.** It fires after every render, including re-renders after zoom and resize. Workaround: debounce it before sending analytics.

## API

- [PageRenderInfo](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/page-render-info/)

---

# Custom toolbar

> Replace the toolbar with your own controls, or combine parts of the built-in toolbar with buttons of your own.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar/

## Basics

`renderToolbar(api)` replaces the toolbar with anything you render, driven by the [viewer API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/).

*Example: Your own controls* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return (
    <PdfViewer
      source="/samples/multipage.pdf"
      toolbar={{ position: 'top' }}
      renderToolbar={(api) => (
        <div className="demo-controls" role="group" aria-label="Document controls">
          <button
            type="button"
            className="demo-button"
            onClick={api.previousPage}
            disabled={api.page <= 1}
          >
            ← Back
          </button>
          <span aria-live="polite">
            Page {api.page} of {api.numPages}
          </span>
          <button
            type="button"
            className="demo-button"
            onClick={api.nextPage}
            disabled={api.page >= api.numPages}
          >
            Next →
          </button>
          <select
            aria-label="Zoom"
            value={String(api.scale)}
            onChange={(event) => api.setScale(Number(event.target.value))}
          >
            {[0.5, 0.75, 1, 1.5, 2].map((scale) => (
              <option key={scale} value={String(scale)}>
                {scale * 100}%
              </option>
            ))}
          </select>
          <button type="button" className="demo-button" onClick={() => void api.download()}>
            Download
          </button>
        </div>
      )}
    />
  );
}
```

The custom toolbar sits where the built-in one would, honoring `toolbar.position`. It re-renders with a fresh `api` whenever page, zoom, rotation or status change.

### Reusing the built-in toolbar

`PdfToolbar` is the built-in toolbar as a component. Render it with a subset of actions next to your own buttons.

*Example: Built-in navigation plus a custom print button* — the source of the live demo on this page.

```tsx
import { PdfToolbar, PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return (
    <PdfViewer
      source="/samples/multipage.pdf"
      renderToolbar={(api) => (
        <div
          style={{
            display: 'flex',
            flexWrap: 'wrap',
            gap: 12,
            alignItems: 'center',
            justifyContent: 'center',
          }}
        >
          <PdfToolbar
            api={api}
            config={{ actions: ['previousPage', 'pageIndicator', 'nextPage'] }}
          />
          <button type="button" onClick={() => void api.print()}>
            Print this report
          </button>
        </div>
      )}
    />
  );
}
```

`PdfToolbar` keeps its accessibility (single tab stop, arrow-key navigation, the responsive **More actions** menu) wherever you place it.

## Customization

`PdfToolbar` takes `config`, `labels`, `locale`, `compact`, `zoomReset`, a replacement `pageIndicator` and `menuPlacement`.

*Example: A wider toolbar with a custom page indicator* — the source of the live demo on this page.

```tsx
import { PdfToolbar, PdfViewer } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

const style = {
  '--rpv-toolbar-bg': '#115e59',
  '--rpv-toolbar-max-width': '640px',
} as CSSProperties;

export default function Demo() {
  return (
    <PdfViewer
      source="/samples/letter-3pages.pdf"
      toolbar={{ position: 'top' }}
      style={style}
      renderToolbar={(api) => (
        <PdfToolbar
          api={api}
          zoomReset
          menuPlacement="bottom"
          labels={{ moreActions: 'More' }}
          pageIndicator={
            <span>
              {api.page} ∕ {api.numPages}
            </span>
          }
        />
      )}
    />
  );
}
```

Every prop is listed in the [`PdfToolbarProps` reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-toolbar-props/); see also the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

Hide the toolbar (`toolbar={false}`) and render controls anywhere in your layout with a [ref](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/), or build a viewer from scratch with the [headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) pieces.

> **Warning: Accessibility is yours in a custom toolbar**
>
> A `renderToolbar` UI replaces the built-in keyboard and screen reader behavior. Label every
>   control, keep disabled states in sync with `canZoomIn`, `canZoomOut`, `page` and `numPages`, and
>   announce page changes, as the first demo does with `aria-live`.

## Limitations

- **One toolbar slot.** `renderToolbar` renders in the toolbar position only. Workaround: hide the toolbar and place controls anywhere with a ref.
- **`PdfToolbar` needs a viewer API.** It cannot drive a headless `PdfPageCanvas` directly. Workaround: build headless controls from `usePdfDocument` state.

## API

- [PdfToolbar](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-toolbar/)
- [PdfToolbarProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-toolbar-props/)

---

# Headless

> Build a completely custom PDF interface from the same document loader and page renderer the viewer uses.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/

`PdfViewer` is assembled from two public building blocks. Use them directly when your layout is not a toolbar and a page: comparison views, galleries, spreads, or pages embedded in a larger UI.

## Basics

`usePdfDocument(source)` loads a document; `PdfPageCanvas` renders one of its pages.

*Example: A thumbnail strip and a fitted page* — the source of the live demo on this page.

```tsx
import { PdfPageCanvas, usePdfDocument } from '@kingtech/react-pdf-viewer';
import { useState } from 'react';

export default function Demo() {
  const { status, document, numPages, error, reload } = usePdfDocument('/samples/multipage.pdf');
  const [page, setPage] = useState(1);

  if (status === 'error') {
    return (
      <p role="alert">
        {error?.message}{' '}
        <button type="button" onClick={reload}>
          Retry
        </button>
      </p>
    );
  }
  if (status !== 'ready' || !document) return <p role="status">Loading…</p>;

  return (
    <div
      style={{ display: 'grid', gridTemplateColumns: '120px 1fr', gap: 16, alignItems: 'start' }}
    >
      <ol
        aria-label="Pages"
        style={{ listStyle: 'none', margin: 0, padding: 0, maxHeight: 520, overflow: 'auto' }}
      >
        {Array.from({ length: numPages }, (_, index) => index + 1).map((n) => (
          <li key={n}>
            <button
              type="button"
              aria-current={n === page ? 'page' : undefined}
              onClick={() => setPage(n)}
              style={{
                border: n === page ? '2px solid #1d4ed8' : '2px solid transparent',
                background: 'none',
                padding: 2,
              }}
            >
              <PdfPageCanvas
                document={document}
                page={n}
                fit={{ width: 96 }}
                aria-label={`Page ${n}`}
              />
            </button>
          </li>
        ))}
      </ol>
      <PdfPageCanvas
        document={document}
        page={page}
        fit={{ width: 520 }}
        aria-label={`Page ${page} of ${numPages}`}
      />
    </div>
  );
}
```

`usePdfDocument` returns `{ status, document, numPages, error, reload }`. It handles fetching, aborting when the source changes, HTTP errors and destroying documents, and never exposes a destroyed document. It accepts the same sources and request options as the viewer.

`PdfPageCanvas` renders with cancellation, double buffering, HiDPI output and the pixel budget. It sizes itself from `scale`, or from a `fit` box (`{ width, height, upscale }`), adds the page's intrinsic rotation to `rotation`, and can render `textLayer` and `annotationLayer`.

## Customization

Combine the pieces into layouts the viewer does not offer, such as a two-page spread with highlighted matches.

*Example: A spread with search highlights* — the source of the live demo on this page.

```tsx
import { PdfPageCanvas, usePdfDocument } from '@kingtech/react-pdf-viewer';
import { useState } from 'react';

/** A two-page spread with highlighted search matches, built from the headless pieces. */
export default function Demo() {
  const { document, numPages } = usePdfDocument('/samples/multipage.pdf');
  const [left, setLeft] = useState(1);
  const [query, setQuery] = useState('viewer');

  if (!document) return <p role="status">Loading…</p>;

  return (
    <>
      <p className="demo-controls">
        <button
          type="button"
          disabled={left <= 1}
          onClick={() => setLeft((p) => Math.max(1, p - 2))}
        >
          ← Previous spread
        </button>
        <button type="button" disabled={left + 2 > numPages} onClick={() => setLeft((p) => p + 2)}>
          Next spread →
        </button>
        <label>
          Highlight <input value={query} onChange={(event) => setQuery(event.target.value)} />
        </label>
      </p>
      <div style={{ display: 'flex', gap: 12, justifyContent: 'center', flexWrap: 'wrap' }}>
        {[left, left + 1]
          .filter((n) => n <= numPages)
          .map((n) => (
            <PdfPageCanvas
              key={n}
              document={document}
              page={n}
              fit={{ width: 320 }}
              textLayer
              highlight={{ query, selected: null }}
              aria-label={`Page ${n} of ${numPages}`}
            />
          ))}
      </div>
    </>
  );
}
```

Style pages with the same `--rpv-page-*` variables as the viewer; see the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Accessibility in headless layouts

Give each `PdfPageCanvas` an `aria-label` (“Page 3 of 10”) so it is exposed as an image, or as a group containing its text and links when `textLayer` or `annotationLayer` is on; without a label it is decorative. Keyboard navigation, focus management and live announcements are yours to provide.

## Advanced / escape hatch

`document` is a PDF.js `PDFDocumentProxy`: anything PDF.js can do (metadata, outline, text extraction, annotations) is available from it.

## Limitations

- **No toolbar, shortcuts or fullscreen.** Headless pieces render pages only. Workaround: use `PdfViewer` with [`renderToolbar`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar/) if you only need a different toolbar.
- **One `usePdfDocument` per document.** Each hook call loads its own copy. Workaround: load once near the top of your tree and pass `document` down.
- **Many canvases cost memory.** Rendering dozens of full-size pages at once is expensive. Workaround: render thumbnails with a small `scale` or `fit`, and mount large pages only when visible.

## API

- [usePdfDocument](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-pdf-document/)
- [UsePdfDocumentOptions](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-pdf-document-options/)
- [UsePdfDocumentResult](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-pdf-document-result/)
- [PdfPageCanvas](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-page-canvas/)
- [PdfPageCanvasProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-page-canvas-props/)

---

# Rendering

> Sharp pages on high-density screens, smooth zooming without flicker, and graceful quality limits on memory-constrained devices.

Features · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/rendering/

## Basics

Pages render to a canvas at the screen's pixel density, so text stays crisp on high-density displays and when the browser is zoomed.

*Example: Zoom quickly: no flicker, no stale pages* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // Zoom in quickly with the + button: each change cancels the previous render, and the old
  // page stays visible until the new one is ready.
  return <PdfViewer source="/samples/letter-3pages.pdf" defaultScale={2} zoomReset />;
}
```

Rendering is built to stay correct under fast interaction:

- **Cancellable.** Starting a new render (another zoom step, a page change) cancels the one in progress, so ten quick clicks produce one finished render.
- **Double-buffered.** The previous page stays on screen until the next render is complete, so zooming never flashes blank.
- **Density-aware.** A change of `devicePixelRatio` (moving the window to another monitor, browser zoom) re-renders at the new density.
- **Rotation-aware.** Each page's intrinsic rotation is applied before yours.

### Pixel budget

`maxCanvasPixels` caps the size of each page's canvas (16,777,216 pixels, 4096 × 4096, by default, the limit iOS Safari enforces). Beyond it, the page keeps its on-screen size but renders at a lower resolution.

*Example: A deliberately small pixel budget* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  // A deliberately tiny budget (250,000 pixels): the page keeps its size but renders at a lower
  // resolution, so it looks soft instead of failing on memory-constrained devices.
  return <PdfViewer source="/samples/letter-3pages.pdf" maxCanvasPixels={250_000} />;
}
```

## Customization

The page surface (background, border, radius and shadow) is styled with CSS variables.

*Example: A paper-like page frame* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

const style = {
  '--rpv-page-bg': '#fffdf7',
  '--rpv-page-border': '1px solid #e7e5e4',
  '--rpv-page-radius': '2px',
  '--rpv-page-shadow': '0 1px 2px rgba(0, 0, 0, 0.08), 0 8px 24px rgba(0, 0, 0, 0.08)',
} as CSSProperties;

export default function Demo() {
  return <PdfViewer source="/samples/letter-3pages.pdf" style={style} />;
}
```

See `--rpv-page-bg`, `--rpv-page-border`, `--rpv-page-radius` and `--rpv-page-shadow` in the [CSS variables reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/css-variables/) and the [customization guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).

## Advanced / escape hatch

[`PdfPageCanvas`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) exposes the renderer directly, with `maxCanvasPixels`, `fit` (see [`FitOptions`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/fit-options/)) and an `onRender` callback reporting the scale actually rendered. For performance advice across the whole viewer, see the [Performance guide](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/performance/).

## Limitations

- **Canvas rendering, not vector.** Pages are bitmaps; extreme zoom levels are limited by the pixel budget. Workaround: raise `maxCanvasPixels` on desktop-only apps.
- **Rendering happens on the main thread.** PDF.js parses in a worker, but painting a complex page can briefly block interaction on slow devices. Workaround: use [continuous layout](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/continuous-layout/), which renders only nearby pages, and avoid very high default zoom on mobile.
- **Color management depends on assets.** Documents with ICC profiles need the ICC assets; see [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/).

## API

- [FitOptions](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/fit-options/)
- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/)

---

# Playground

> Try every option of the viewer live, on bundled samples, your own URL or a local file, and watch its events.

Demos · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/demos/playground/

## What this shows

Every switch in the options panel maps to one prop, so you can see the effect of a feature before you read its page:

- Loading bundled samples, a URL or a local file: [Document sources](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/document-sources/) and [Authenticated requests](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/authenticated-requests/).
- Controlled page, scale and rotation with the imperative API buttons: [Controlled state](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/controlled-state/) and [Imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/).
- Toolbar position, the compact breakpoint and the responsive “More actions” menu (drag the container width): [Toolbar](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/toolbar/).
- Native, overlay and controlled fullscreen: [Fullscreen](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/fullscreen/).
- Fit modes, zoom presets, zoom reset and Ctrl/⌘ + wheel zoom: [Fit modes](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/fit-modes/) and [Zoom](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/zoom/).
- Continuous layout and thumbnails: [Continuous layout](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/continuous-layout/) and [Thumbnails](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/thumbnails/).
- Text layer, links and search: [Text layer](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/text-layer/), [Links](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/links/) and [Search](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/search/).
- Page number input and the password prompt: [Page number input](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/page-number-input/) and [Password protection](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/password-protection/).
- Keyboard shortcuts and print modes: [Keyboard shortcuts](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/keyboard-shortcuts/) and [Print](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/print/).
- Dark and custom themes and Hungarian labels: [Theming](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/theming/) and [Labels and locale](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/labels/).
- The event log records `onPageChange`, `onScaleChange`, `onDocumentLoad`, `onPageRender` and `onError`: [Events](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/events/).

## Source

The playground is a regular React component built on the public API:
[apps/site/src/islands/Playground.tsx](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/apps/site/src/islands/Playground.tsx).

## Try it with your own data

Paste any CORS-enabled PDF URL into the **URL** tab, or drop a PDF anywhere on the page. Local files never leave your browser.

---

# API reference

> Generated reference for every public component, hook, function and type.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/

## Components

- [PdfPageCanvas](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-page-canvas.md): Renders a single PDF page: cancellable, double-buffered, HiDPI and pixel-capped, with optional text and annotation layers.
- [PdfToolbar](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-toolbar.md): The default toolbar: zoom, page navigation, fullscreen, rotate, download and print, on one row.
- [PdfViewer](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer.md): A PDF viewer: one page at a time with a toolbar for zoom, navigation, fullscreen, rotation, download and print.

## Hooks

- [useControllableState](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-controllable-state.md): The value / defaultValue / onValueChange pattern as a hook: the one behind every controlled prop of the viewer.
- [usePdfDocument](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-pdf-document.md): Loads a PDF document. Every source change aborts the previous fetch, discards its result and destroys the previous document; unmounting does the same.

## Functions

- [configurePdfJs](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/configure-pdf-js.md): Configures PDF.js for every viewer instance. Call it once at startup, before the first viewer mounts. Calls merge with previous configuration.

## Types

- [defaultLabels](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/default-labels.md): The default English labels for every user-visible string and accessible name. Override any subset with the labels prop.
- [FitOptions](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/fit-options.md): Constrains a rendered page to a box: fit its width, its height or both, and choose whether it may grow beyond its natural size.
- [FullscreenMode](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/fullscreen-mode.md): How fullscreen is presented: the browser Fullscreen API, a full-viewport overlay, or layout only while the parent presents it.
- [LabelContext](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/label-context.md): Helpers passed to label functions, such as locale-aware number formatting, so custom labels follow the viewer's locale.
- [PageHighlight](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/page-highlight.md): Search highlighting for one page's text layer: the query to mark, and which of the page's matches is the selected one.
- [PageRenderInfo](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/page-render-info.md): Details of a completed page render, passed to onPageRender and onRender: the page, the scale used, its size and the time it took.
- [PdfDocumentStatus](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-document-status.md): Lifecycle status of a document load: idle without a source, loading, ready with a document, or error with a PdfViewerError.
- [PdfJsConfig](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-js-config.md): Global PDF.js configuration shared by every viewer: how the module is loaded, the worker, and the URLs of its assets.
- [PdfJsDocumentOptions](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-js-document-options.md): Per-instance options forwarded to pdfjs-dist's getDocument (for example cMapUrl, standardFontDataUrl or verbosity). Source-related options are managed by the viewer.
- [PdfJsModule](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-js-module.md): The pdfjs-dist module namespace, as returned by the loader of configurePdfJs to supply a specific build.
- [PdfPageCanvasProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-page-canvas-props.md): Props for PdfPageCanvas: the document and page to render, its scale, rotation and fit, the optional layers and the render callbacks.
- [PdfRequestOptions](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-request-options.md): Controls how string and URL sources are fetched: extra fetch options, a replacement fetcher and the handling of HTTP errors.
- [PdfSource](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-source.md): A PDF document source: a URL for the viewer to fetch, or a file or bytes that are already in memory.
- [PdfToolbarProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-toolbar-props.md): Props for PdfToolbar: the viewer API it controls, its labels and configuration, the compact layout and the page indicator slot.
- [PdfViewerApi](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-api.md): Imperative viewer API, available through ref and passed to renderToolbar. Values are a snapshot of the render that produced the object.
- [PdfViewerError](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-error.md): A typed viewer error. Aborted loads and canceled renders are never reported as errors.
- [PdfViewerErrorCode](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-error-code.md): Machine-readable category of a PdfViewerError, for handling failures such as a missing password or an HTTP error in code.
- [PdfViewerLabels](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-labels.md): Every user-visible string and accessible name. Defaults are English; override any subset through the labels prop.
- [PdfViewerProps](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props.md): Props for PdfViewer. Every piece of state supports the value / defaultValue / onChange trio.
- [Rotation](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/rotation.md): Clockwise rotation in degrees, relative to the page's natural orientation.
- [SetControllableState](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/set-controllable-state.md): Setter returned by useControllableState; accepts a value or an updater function.
- [ToolbarAction](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/toolbar-action.md): Identifies a toolbar control, for choosing which controls the toolbar shows and in which order through ToolbarConfig.
- [ToolbarConfig](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/toolbar-config.md): Toolbar customization: where the toolbar sits, which controls it shows and in which order, and which it hides on narrow screens.
- [UseControllableStateOptions](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-controllable-state-options.md): Options for useControllableState: the controlled value, the initial value while uncontrolled, and the change callback.
- [UsePdfDocumentOptions](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-pdf-document-options.md): Options for usePdfDocument. Changes apply on the next load, except password.
- [UsePdfDocumentResult](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-pdf-document-result.md): State returned by usePdfDocument: the load status, the document once ready, its page count, the error and a reload function.

## Settings

- [CSS variables](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/css-variables.md): Every custom property the stylesheet reads, with its fallback value. Set them on the viewer or any ancestor.
- [rpv-theme-auto](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/rpv-theme-auto.md): Automatic preset: add className="rpv-theme-auto" to apply the dark values only when the operating system prefers a dark color scheme.
- [rpv-theme-dark](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/rpv-theme-dark.md): Dark preset: add className="rpv-theme-dark" to the viewer to apply these values.

## Commands

- [Keyboard shortcuts](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/keyboard-shortcuts.md): Keys the component handles, generated from @shortcut tags next to the implementation.

---

# configurePdfJs reference

> Configures PDF.js for every viewer instance. Call it once at startup, before the first viewer mounts. Calls merge with previous configuration.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/configure-pdf-js/

## Used by

- [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration.md)

## Import

```ts
import { configurePdfJs } from '@kingtech/react-pdf-viewer';
```

Signature: `configurePdfJs(next: PdfJsConfig): void` → `void`

## Options

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `loader` | `() => Promise<module>` | `() => import('pdfjs-dist')` | No | Returns the `pdfjs-dist` module, e.g. `() =&gt; import('pdfjs-dist/legacy/build/pdf.mjs')` for older browsers. |
| `workerSrc` | `string` | `the jsDelivr worker for the installed version` | No | Worker script URL. A worker you set on `GlobalWorkerOptions` yourself is respected. |
| `workerPort` | `Worker` | — | No | A ready `Worker` running the PDF.js worker; takes precedence over `workerSrc`. |
| `cMapUrl` | `string` | `jsDelivr cmaps/ for the installed version` | No | CMap files, trailing slash. |
| `cMapPacked` | `boolean` | `true` | No | Whether the CMaps are packed. |
| `standardFontDataUrl` | `string` | `jsDelivr standard_fonts/ for the installed version` | No | Standard font data, trailing slash. |
| `wasmUrl` | `string` | `jsDelivr wasm/ for the installed version` | No | WebAssembly image decoders, trailing slash. |
| `iccUrl` | `string` | `jsDelivr iccs/ for the installed version` | No | ICC color profiles, trailing slash. |
| `isEvalSupported` | `boolean` | `false` | No | Forwarded to `getDocument`; kept `false` as defense in depth (CVE-2024-4367). |

## Source

[packages/react-pdf-viewer/src/core/pdfjs.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/core/pdfjs.ts#L106)

---

# CSS variables reference

> Every custom property the stylesheet reads, with its fallback value. Set them on the viewer or any ancestor.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/css-variables/

## Used by

- [Theming](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/theming.md)

## Import

```ts
import '@kingtech/react-pdf-viewer/styles.css';
```

## Settings

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `--rpv-accent` | `CSS value` | `#1976d2` | No | Accent: focus outlines outside the toolbar, progress, the current thumbnail. |
| `--rpv-button-disabled-fg` | `CSS value` | `rgba(255, 255, 255, 0.3)` | No | Disabled button color. |
| `--rpv-button-hover-bg` | `CSS value` | `rgba(255, 255, 255, 0.1)` | No | Button hover background. |
| `--rpv-button-size` | `CSS value` | `34px` | No | Icon button size. |
| `--rpv-error-bg` | `CSS value` | `#fdeded` | No | Error view background. |
| `--rpv-error-fg` | `CSS value` | `#5f2120` | No | Error view text. |
| `--rpv-error-icon` | `CSS value` | `#d32f2f` | No | Error view icon. |
| `--rpv-focus-ring` | `CSS value` | `2px solid #ffffff` | No | Focus outline of toolbar controls. |
| `--rpv-focus-ring-offset` | `CSS value` | `2px` | No | Focus outline offset. |
| `--rpv-font-family` | `CSS value` | `inherit` | No | Font of all viewer text. |
| `--rpv-fullscreen-bg` | `CSS value` | `#ffffff` | No | Fullscreen background. |
| `--rpv-icon-size` | `CSS value` | `24px` | No | Icon size. |
| `--rpv-label-font-size` | `CSS value` | `14px` | No | Zoom and page labels. |
| `--rpv-label-font-weight` | `CSS value` | `600` | No | Zoom and page labels. |
| `--rpv-menu-bg` | `CSS value` | `#ffffff` | No | Menu surface. |
| `--rpv-menu-border` | `CSS value` | `rgb(0 0 0 / 0.08)` | No | Menu border color. |
| `--rpv-menu-fg` | `CSS value` | `#1f2a30` | No | Menu item text. |
| `--rpv-menu-hover-bg` | `CSS value` | `rgb(84 100 110 / 0.12)` | No | Hovered and focused item. |
| `--rpv-menu-icon-fg` | `CSS value` | `#54646e` | No | Menu item icons. |
| `--rpv-menu-shadow` | `CSS value` | `0 10px 28px rgb(15 23 42 / 0.18),       0 2px 6px rgb(15 23 42 / 0.12)` | No | Menu elevation. |
| `--rpv-muted-fg` | `CSS value` | `rgba(0, 0, 0, 0.6)` | No | Secondary text. |
| `--rpv-overlay-z-index` | `CSS value` | `1300` | No | Stacking order of `overlay` fullscreen. |
| `--rpv-page-bg` | `CSS value` | `#ffffff` | No | Page background (and thumbnail placeholders). |
| `--rpv-page-border` | `CSS value` | `1px solid #dddddd` | No | Page border. |
| `--rpv-page-gap` | `CSS value` | `16px` | No | Space between pages (`layout="continuous"`). |
| `--rpv-page-input-bg` | `CSS value` | `rgb(0 0 0 / 0.22)` | No | Page-number input fill (`pageInput`). |
| `--rpv-page-input-border` | `CSS value` | `rgb(255 255 255 / 0.35)` | No | Page-number input border color. |
| `--rpv-page-input-chars` | `CSS value` | `2` | No | Width of the page-number input in characters; set automatically. |
| `--rpv-page-radius` | `CSS value` | `4px` | No | Page corner radius. |
| `--rpv-page-shadow` | `CSS value` | `0 2px 8px rgba(0, 0, 0, 0.1)` | No | Page shadow. |
| `--rpv-search-bg` | `CSS value` | `#ffffff` | No | Search bar background. |
| `--rpv-search-fg` | `CSS value` | `#37474f` | No | Search bar text. |
| `--rpv-search-highlight` | `CSS value` | `rgb(255 213 0 / 0.5)` | No | Search matches. |
| `--rpv-search-highlight-selected` | `CSS value` | `rgb(255 111 0 / 0.6)` | No | The current match. |
| `--rpv-selection-bg` | `CSS value` | `rgb(0 0 255 / 0.25)` | No | Text selection (`textLayer`). |
| `--rpv-text-button-fg` | `CSS value` | `#1565c0` | No | Retry and Cancel text buttons. |
| `--rpv-thumbnails-width` | `CSS value` | `148px` | No | Thumbnails sidebar width. |
| `--rpv-toolbar-bg` | `CSS value` | `#54646e` | No | Toolbar background. |
| `--rpv-toolbar-fg` | `CSS value` | `#ffffff` | No | Toolbar icons and text. |
| `--rpv-toolbar-gap` | `CSS value` | `8px` | No | Space between controls within a group. |
| `--rpv-toolbar-group-gap` | `CSS value` | `16px` | No | Space between the zoom, page and action groups when `pageInput` or `zoomReset` is on. |
| `--rpv-toolbar-margin-bottom` | `CSS value` | `40px` | No | Space below the toolbar. |
| `--rpv-toolbar-max-width` | `CSS value` | `482px` | No | Width cap. |
| `--rpv-toolbar-min-height` | `CSS value` | `48px` | No | Minimum height. |
| `--rpv-toolbar-padding-x` | `CSS value` | `16px` | No | Horizontal padding. |
| `--rpv-toolbar-radius` | `CSS value` | `8px` | No | Corner radius. |
| `--rpv-viewport-margin-bottom` | `CSS value` | `64px` | No | Space between the document and the toolbar. |
| `--rpv-viewport-max-height` | `CSS value` | `800px` | No | Document area height cap. |
| `--rpv-viewport-max-width` | `CSS value` | `650px` | No | Document area width cap. |
| `--rpv-viewport-min-height` | `CSS value` | `712px` | No | Document area minimum height. |
| `--rpv-viewport-min-height-compact` | `CSS value` | `400px` | No | Minimum height in compact mode. |
| `--rpv-viewport-padding` | `CSS value` | `0px` | No | Document area padding. |
| `--rpv-viewport-padding-compact` | `CSS value` | `16px` | No | Padding in compact mode. |
| `--rpv-viewport-padding-fullscreen` | `CSS value` | `32px` | No | Padding in fullscreen. |

## Source

[packages/react-pdf-viewer/src/styles/pdf-viewer.css](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/styles/pdf-viewer.css#L90)

---

# defaultLabels reference

> The default English labels for every user-visible string and accessible name. Override any subset with the labels prop.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/default-labels/

## Used by

- [Labels and locale](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/labels.md)

## Import

```ts
import { defaultLabels } from '@kingtech/react-pdf-viewer';
```

Signature: `const defaultLabels: PdfViewerLabels`

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `viewer` | `string` | `'PDF viewer'` | Yes | Accessible name of the viewer region. |
| `toolbar` | `string` | `'PDF controls'` | Yes | Accessible name of the toolbar. |
| `document` | `string` | `'Document'` | Yes | Accessible name of the scrollable document area. |
| `zoomOut` | `string` | `'Zoom out'` | Yes | Button. |
| `zoomIn` | `string` | `'Zoom in'` | Yes | Button. |
| `zoomLevel` | `(percent: number, context: LabelContext) => string` | `function` | Yes | Zoom label text. |
| `resetZoom` | `string` | `'Reset zoom'` | Yes | Reset-zoom button (with `zoomReset`) and menu item. |
| `previousPage` | `string` | `'Previous page'` | Yes | Button. |
| `nextPage` | `string` | `'Next page'` | Yes | Button. |
| `pageIndicator` | `(page: number, total: number, context: LabelContext) => string` | `function` | Yes | Page label text. |
| `pageAriaLabel` | `(page: number, total: number, context: LabelContext) => string` | `function` | Yes | Accessible name of each page. |
| `pageInput` | `string` | `'Page number'` | Yes | Accessible name of the page-number input. |
| `enterFullscreen` | `string` | `'Enter fullscreen'` | Yes | Fullscreen button outside fullscreen. |
| `exitFullscreen` | `string` | `'Exit fullscreen'` | Yes | Fullscreen button in fullscreen. |
| `rotate` | `string` | `'Rotate PDF'` | Yes | Button. |
| `download` | `string` | `'Download PDF'` | Yes | Button. |
| `print` | `string` | `'Print PDF'` | Yes | Button. |
| `moreActions` | `string` | `'More actions'` | Yes | Overflow menu button shown when the toolbar is too narrow. |
| `loading` | `string` | `'Loading PDF...'` | Yes | Loading view text. |
| `retry` | `string` | `'Retry'` | Yes | Retry button of the error view. |
| `empty` | `string` | `'No document'` | Yes | Announced when there is no document. |
| `printProgress` | `(done: number, total: number, context: LabelContext) => string` | `function` | Yes | Print preparation status. |
| `cancel` | `string` | `'Cancel'` | Yes | Cancels print preparation. |
| `passwordPrompt` | `string` | `'This document is password protected'` | Yes | Password form heading. |
| `passwordLabel` | `string` | `'Password'` | Yes | Password field label. |
| `passwordSubmit` | `string` | `'Open'` | Yes | Password submit button. |
| `passwordIncorrect` | `string` | `'Incorrect password. Please try again.'` | Yes | Shown after a wrong password. |
| `thumbnails` | `string` | `'Pages'` | Yes | Accessible name of the thumbnails sidebar. |
| `search` | `string` | `'Search in document'` | Yes | Accessible name of the search field. |
| `searchResults` | `(current: number, total: number, context: LabelContext) => string` | `function` | Yes | Search result status. |
| `searchPrevious` | `string` | `'Previous match'` | Yes | Search button. |
| `searchNext` | `string` | `'Next match'` | Yes | Search button. |
| `searchNoResults` | `string` | `'No matches'` | Yes | Shown when nothing matches. |

## Source

[packages/react-pdf-viewer/src/labels.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/labels.ts#L64)

---

# FitOptions reference

> Constrains a rendered page to a box: fit its width, its height or both, and choose whether it may grow beyond its natural size.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/fit-options/

## Used by

- [Rendering](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/rendering.md)

## Import

```ts
import type { FitOptions } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `width` | `number` | — | No | Available width in CSS pixels, including border and padding. |
| `height` | `number` | — | No | Available height in CSS pixels, including border and padding. |
| `upscale` | `boolean` | — | No | Scale up to fill the box; otherwise the page only ever shrinks. |

## Source

[packages/react-pdf-viewer/src/core/geometry.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/core/geometry.ts#L48)

---

# FullscreenMode reference

> How fullscreen is presented: the browser Fullscreen API, a full-viewport overlay, or layout only while the parent presents it.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/fullscreen-mode/

- `'controlled'`: layout-only; the parent decides how to present fullscreen.
- `'native'`: the browser Fullscreen API on the viewer root, falling back to `'overlay'`.
- `'overlay'`: a fixed, full-viewport layer with a focus trap; `Esc` closes it.

## Used by

- [Fullscreen](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/fullscreen.md)

## Import

```ts
import type { FullscreenMode } from '@kingtech/react-pdf-viewer';
```

Signature: `type FullscreenMode = 'controlled' | 'native' | 'overlay'`

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `controlled` | `'controlled'` | — | No | Layout only: the parent decides how to present fullscreen. |
| `native` | `'native'` | — | No | The browser Fullscreen API on the viewer root, falling back to `'overlay'`. |
| `overlay` | `'overlay'` | — | No | A fixed, full-viewport layer with a focus trap and scroll lock; `Esc` closes it. |

## Source

[packages/react-pdf-viewer/src/types.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/types.ts#L35)

---

# Keyboard shortcuts reference

> Keys the component handles, generated from @shortcut tags next to the implementation.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/keyboard-shortcuts/

## Used by

- [Keyboard shortcuts](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/keyboard-shortcuts.md)

## Import

```ts
```

## Commands

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `0` | `0` | — | No | Reset zoom to `defaultScale`. |
| `PageUp` | `PageUp` | — | No | Previous page. |
| `PageDown` | `PageDown` | — | No | Next page. |
| `ArrowLeft` | `ArrowLeft` | — | No | Previous page; a zoomed page scrolls horizontally first. |
| `ArrowRight` | `ArrowRight` | — | No | Next page; a zoomed page scrolls horizontally first. |
| `Home` | `Home` | — | No | First page. |
| `End` | `End` | — | No | Last page. |
| `+` | ` + ` | — | No | Zoom in (`=` also works). |
| `-` | `-` | — | No | Zoom out (`_` also works). |
| `r` | `r` | — | No | Rotate clockwise. |
| `Shift+R` | `Shift + R` | — | No | Rotate counter-clockwise. |
| `f` | `f` | — | No | Toggle fullscreen. |
| `Ctrl+F` | `Ctrl + F` | — | No | Focus the search field (`⌘+F` on macOS); `Enter` and `Shift+Enter` step through matches, `Escape` clears. |

## Source

[packages/react-pdf-viewer/src/types.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/types.ts#L271)

---

# LabelContext reference

> Helpers passed to label functions, such as locale-aware number formatting, so custom labels follow the viewer's locale.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/label-context/

## Used by

- [Labels and locale](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/labels.md)

## Import

```ts
import type { LabelContext } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `formatNumber` | `(value: number) => string` | — | Yes | Formats an integer for the viewer's `locale` (no grouping separators). |

## Source

[packages/react-pdf-viewer/src/labels.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/labels.ts#L4)

---

# PageHighlight reference

> Search highlighting for one page's text layer: the query to mark, and which of the page's matches is the selected one.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/page-highlight/

## Used by

- [Search](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/search.md)

## Import

```ts
import type { PageHighlight } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `query` | `string` | — | Yes | Text to highlight, case-insensitive. |
| `selected` | `number \| null` | — | Yes | Index of the page’s match to mark as selected. |

## Source

[packages/react-pdf-viewer/src/hooks/usePageRenderer.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/hooks/usePageRenderer.ts#L46)

---

# PageRenderInfo reference

> Details of a completed page render, passed to onPageRender and onRender: the page, the scale used, its size and the time it took.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/page-render-info/

## Used by

- [Events](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/events.md)

## Import

```ts
import type { PageRenderInfo } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `page` | `number` | — | Yes | The page rendered (after clamping). |
| `scale` | `number` | — | Yes | The scale actually rendered (after fitting). |
| `width` | `number` | — | Yes | Rendered CSS width in pixels. |
| `height` | `number` | — | Yes | Rendered CSS height in pixels. |
| `durationMs` | `number` | — | Yes | Time from request to completion. |

## Source

[packages/react-pdf-viewer/src/hooks/usePageRenderer.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/hooks/usePageRenderer.ts#L32)

---

# PdfDocumentStatus reference

> Lifecycle status of a document load: idle without a source, loading, ready with a document, or error with a PdfViewerError.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-document-status/

## Used by

- [Loading states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/loading-states.md)

## Import

```ts
import type { PdfDocumentStatus } from '@kingtech/react-pdf-viewer';
```

Signature: `type PdfDocumentStatus = 'idle' | 'loading' | 'ready' | 'error'`

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `idle` | `'idle'` | — | No | No source, or an HTTP error that `onHttpError` handled. |
| `loading` | `'loading'` | — | No | Fetching or parsing the document. |
| `ready` | `'ready'` | — | No | The document is loaded and pages can render. |
| `error` | `'error'` | — | No | Loading failed; the error is available. |

## Source

[packages/react-pdf-viewer/src/types.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/types.ts#L57)

---

# PdfJsConfig reference

> Global PDF.js configuration shared by every viewer: how the module is loaded, the worker, and the URLs of its assets.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-js-config/

Every field is optional. Unset asset URLs default to the jsDelivr CDN copy of the exact installed `pdfjs-dist` version; self-host them for production, as described in PDF.js configuration.

## Used by

- [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration.md)

## Import

```ts
import type { PdfJsConfig } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `loader` | `() => Promise<module>` | `() => import('pdfjs-dist')` | No | Returns the `pdfjs-dist` module, e.g. `() =&gt; import('pdfjs-dist/legacy/build/pdf.mjs')` for older browsers. |
| `workerSrc` | `string` | `the jsDelivr worker for the installed version` | No | Worker script URL. A worker you set on `GlobalWorkerOptions` yourself is respected. |
| `workerPort` | `Worker` | — | No | A ready `Worker` running the PDF.js worker; takes precedence over `workerSrc`. |
| `cMapUrl` | `string` | `jsDelivr cmaps/ for the installed version` | No | CMap files, trailing slash. |
| `cMapPacked` | `boolean` | `true` | No | Whether the CMaps are packed. |
| `standardFontDataUrl` | `string` | `jsDelivr standard_fonts/ for the installed version` | No | Standard font data, trailing slash. |
| `wasmUrl` | `string` | `jsDelivr wasm/ for the installed version` | No | WebAssembly image decoders, trailing slash. |
| `iccUrl` | `string` | `jsDelivr iccs/ for the installed version` | No | ICC color profiles, trailing slash. |
| `isEvalSupported` | `boolean` | `false` | No | Forwarded to `getDocument`; kept `false` as defense in depth (CVE-2024-4367). |

## Source

[packages/react-pdf-viewer/src/core/pdfjs.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/core/pdfjs.ts#L14)

---

# PdfJsDocumentOptions reference

> Per-instance options forwarded to pdfjs-dist's getDocument (for example cMapUrl, standardFontDataUrl or verbosity). Source-related options are managed by the viewer.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-js-document-options/

## Used by

- [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration.md)

## Import

```ts
import type { PdfJsDocumentOptions } from '@kingtech/react-pdf-viewer';
```

Signature: `type PdfJsDocumentOptions = Omit<DocumentInitParameters, 'url' | 'data' | 'password' | 'range' | 'worker' | 'httpHeaders' | 'withCredentials'>`

## Source

[packages/react-pdf-viewer/src/types.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/types.ts#L110)

---

# PdfJsModule reference

> The pdfjs-dist module namespace, as returned by the loader of configurePdfJs to supply a specific build.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-js-module/

## Used by

- [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration.md)

## Import

```ts
import type { PdfJsModule } from '@kingtech/react-pdf-viewer';
```

Signature: `type PdfJsModule = typeof PdfJsNamespace`

## Source

[packages/react-pdf-viewer/src/core/pdfjs.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/core/pdfjs.ts#L7)

---

# PdfPageCanvas reference

> Renders a single PDF page: cancellable, double-buffered, HiDPI and pixel-capped, with optional text and annotation layers.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-page-canvas/

The building block for custom viewers, together with `usePdfDocument`.

## Used by

- [Headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless.md)

## Import

```ts
import { PdfPageCanvas } from '@kingtech/react-pdf-viewer';
```

## Props

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `document` | `PDFDocumentProxy` | — | Yes | A loaded document, e.g. from `usePdfDocument`. |
| `page` | `number` | — | Yes | 1-based page; clamped to the document. |
| `scale` | `number` | `1` | No | Scale, `1` = 100%. |
| `rotation` | `Rotation` | `0` | No | Added to the page’s intrinsic rotation. |
| `fit` | `FitOptions` | — | No | A box the page must fit into. |
| `maxCanvasPixels` | `number` | `16_777_216` | No | Canvas pixel budget. |
| `textLayer` | `boolean` | `false` | No | Selectable text layer. |
| `annotationLayer` | `boolean` | `false` | No | Clickable links. |
| `highlight` | `PageHighlight` | — | No | Search matches to highlight (requires `textLayer`). |
| `aria-label` | `string` | — | No | Accessible name, e.g. `Page 1 of 3`. With a name the page is an image (a group with `textLayer`); without one it is decorative. |
| `className` | `string` | — | No | Added to the page element. |
| `style` | `CSSProperties` | — | No | Inline styles on the page element. |
| `renderError` | `(error: PdfViewerError, actions: { retry: () => void }) => ReactNode` | — | No | Replaces the in-place error view. |

## Events

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `onLinkNavigate` | `(page: number) => void` | — | No | An internal link targets another page. |
| `onHighlight` | `(element: HTMLElement) => void` | — | No | Receives the selected match’s element, e.g. to scroll it into view. |
| `onRender` | `(info: PageRenderInfo) => void` | — | No | After each completed render. |
| `onError` | `(error: PdfViewerError) => void` | — | No | Rendering failed (cancellations are not failures). |

## Source

[packages/react-pdf-viewer/src/components/PdfPageCanvas.tsx](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/components/PdfPageCanvas.tsx#L115)

---

# PdfPageCanvasProps reference

> Props for PdfPageCanvas: the document and page to render, its scale, rotation and fit, the optional layers and the render callbacks.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-page-canvas-props/

## Used by

- [Headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless.md)

## Import

```ts
import type { PdfPageCanvasProps } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `document` | `PDFDocumentProxy` | — | Yes | A loaded document, e.g. from `usePdfDocument`. |
| `page` | `number` | — | Yes | 1-based page; clamped to the document. |
| `scale` | `number` | `1` | No | Scale, `1` = 100%. |
| `rotation` | `Rotation` | `0` | No | Added to the page’s intrinsic rotation. |
| `fit` | `FitOptions` | — | No | A box the page must fit into. |
| `maxCanvasPixels` | `number` | `16_777_216` | No | Canvas pixel budget. |
| `textLayer` | `boolean` | `false` | No | Selectable text layer. |
| `annotationLayer` | `boolean` | `false` | No | Clickable links. |
| `highlight` | `PageHighlight` | — | No | Search matches to highlight (requires `textLayer`). |
| `onLinkNavigate` | `(page: number) => void` | — | No | An internal link targets another page. |
| `onHighlight` | `(element: HTMLElement) => void` | — | No | Receives the selected match’s element, e.g. to scroll it into view. |
| `aria-label` | `string` | — | No | Accessible name, e.g. `Page 1 of 3`. With a name the page is an image (a group with `textLayer`); without one it is decorative. |
| `className` | `string` | — | No | Added to the page element. |
| `style` | `CSSProperties` | — | No | Inline styles on the page element. |
| `renderError` | `(error: PdfViewerError, actions: { retry: () => void }) => ReactNode` | — | No | Replaces the in-place error view. |
| `onRender` | `(info: PageRenderInfo) => void` | — | No | After each completed render. |
| `onError` | `(error: PdfViewerError) => void` | — | No | Rendering failed (cancellations are not failures). |

## Source

[packages/react-pdf-viewer/src/components/PdfPageCanvas.tsx](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/components/PdfPageCanvas.tsx#L12)

---

# PdfRequestOptions reference

> Controls how string and URL sources are fetched: extra fetch options, a replacement fetcher and the handling of HTTP errors.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-request-options/

## Used by

- [Authenticated requests](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/authenticated-requests.md)

## Import

```ts
import type { PdfRequestOptions } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `requestInit` | `RequestInit` | — | No | Extra `fetch` options for URL sources: headers, `credentials`, `mode`, … |
| `fetcher` | `(url: string, init: RequestInit) => Promise<Response>` | — | No | Replaces `fetch` entirely, e.g. with an authenticated HTTP client. |
| `onHttpError` | `(response: Response) => boolean \| void \| Promise<boolean \| void>` | — | No | Called for non-2xx responses. Return `true` when handled (e.g. after redirecting to sign-in): the viewer shows its empty state and does not call `onError`. |
| `getHttpErrorMessage` | `(response: Response) => Promise<string>` | — | No | Builds the `HTTP_ERROR` message. Return `undefined` for the default: an RFC 7807 `detail`, then `title`, then a JSON string body, then `Failed to fetch PDF: {status} {statusText}`. |

## Source

[packages/react-pdf-viewer/src/types.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/types.ts#L116)

---

# PdfSource reference

> A PDF document source: a URL for the viewer to fetch, or a file or bytes that are already in memory.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-source/

- `string` / `URL`: fetched by the viewer (see `PdfRequestOptions`).
- `ArrayBuffer` / `Uint8Array` / `Blob` (including `File`): read directly, nothing is uploaded.

Strings compare by value; every other source compares by identity, so keep binary sources referentially stable (e.g. in state) to avoid reloading on every render.

## Used by

- [Document sources](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/document-sources.md)

## Import

```ts
import type { PdfSource } from '@kingtech/react-pdf-viewer';
```

Signature: `type PdfSource = string | URL | ArrayBuffer | Uint8Array | Blob`

## Source

[packages/react-pdf-viewer/src/types.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/types.ts#L15)

---

# PdfToolbar reference

> The default toolbar: zoom, page navigation, fullscreen, rotate, download and print, on one row.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-toolbar/

When the row does not fit, secondary actions collapse into a “More actions” menu. Usable on its own, for example inside `renderToolbar`.

## Used by

- [Custom toolbar](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar.md)

## Import

```ts
import { PdfToolbar } from '@kingtech/react-pdf-viewer';
```

## Props

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `api` | `PdfViewerApi` | — | Yes | The viewer API, from a `ref` or `renderToolbar`. |
| `labels` | `Partial<PdfViewerLabels>` | — | No | Label overrides. |
| `config` | `ToolbarConfig` | — | No | Which actions to show. |
| `compact` | `boolean` | — | No | Compact layout: hides `hiddenWhenCompact` actions outside fullscreen. |
| `locale` | `string` | — | No | Locale for number formatting. |
| `zoomReset` | `boolean` | `false` | No | The zoom label becomes a reset button. |
| `pageIndicator` | `ReactNode` | — | No | Replaces the page indicator text, e.g. with your own input. |
| `menuPlacement` | `'top' \| 'bottom'` | — | No | Where the More actions menu opens. `PdfViewer` picks the side away from the edge. |

## Source

[packages/react-pdf-viewer/src/components/Toolbar.tsx](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/components/Toolbar.tsx#L412)

---

# PdfToolbarProps reference

> Props for PdfToolbar: the viewer API it controls, its labels and configuration, the compact layout and the page indicator slot.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-toolbar-props/

## Used by

- [Custom toolbar](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar.md)

## Import

```ts
import type { PdfToolbarProps } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `api` | `PdfViewerApi` | — | Yes | The viewer API, from a `ref` or `renderToolbar`. |
| `labels` | `Partial<PdfViewerLabels>` | — | No | Label overrides. |
| `config` | `ToolbarConfig` | — | No | Which actions to show. |
| `compact` | `boolean` | — | No | Compact layout: hides `hiddenWhenCompact` actions outside fullscreen. |
| `locale` | `string` | — | No | Locale for number formatting. |
| `zoomReset` | `boolean` | `false` | No | The zoom label becomes a reset button. |
| `pageIndicator` | `ReactNode` | — | No | Replaces the page indicator text, e.g. with your own input. |
| `menuPlacement` | `'top' \| 'bottom'` | — | No | Where the More actions menu opens. `PdfViewer` picks the side away from the edge. |

## Source

[packages/react-pdf-viewer/src/components/Toolbar.tsx](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/components/Toolbar.tsx#L95)

---

# PdfViewer reference

> A PDF viewer: one page at a time with a toolbar for zoom, navigation, fullscreen, rotation, download and print.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer/

Default props give a classic single-page layout; every additional feature (text layer, links, continuous layout, thumbnails, search, …) is opt-in.

## Used by

- [Document sources](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/document-sources.md)

## Import

```ts
import { PdfViewer } from '@kingtech/react-pdf-viewer';
```

## Props

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `source` | `PdfSource \| null` | — | Yes | The document: a URL string or `URL`, or `ArrayBuffer` / `Uint8Array` / `Blob` / `File`. `null`, `undefined` and `''` show the empty state. |
| `fileName` | `string` | `derived from the source` | No | Download file name. Default: a `File`’s name, the URL’s last `.pdf` segment, else `document.pdf`. |
| `page` | `number` | — | No | Controlled 1-based page. Out-of-range values are clamped and reported through `onPageChange`. |
| `defaultPage` | `number` | `1` | No | Initial page while uncontrolled, and the page every new document opens on. |
| `scale` | `number` | — | No | Controlled scale: `1` = 100%, one PDF point per CSS pixel. |
| `defaultScale` | `number` | `1` | No | Initial scale while uncontrolled; also the reset-zoom target and the scale at which fitting applies. |
| `rotation` | `Rotation` | — | No | Controlled clockwise rotation, added to each page’s intrinsic rotation. |
| `defaultRotation` | `Rotation` | `0` | No | Initial rotation while uncontrolled. |
| `fullscreen` | `boolean` | — | No | Controlled fullscreen state. Passing it selects `fullscreenMode="controlled"` unless a mode is set. |
| `defaultFullscreen` | `boolean` | `false` | No | Initial fullscreen state while uncontrolled. |
| `fullscreenMode` | `FullscreenMode` | `'controlled' when fullscreen is passed, else 'native'` | No | `controlled`: layout only, the parent presents fullscreen (e.g. in its own dialog). `native`: the browser Fullscreen API on the viewer root, falling back to `overlay`. `overlay`: a fixed full-viewport layer with a focus trap; `Esc` closes it. |
| `minScale` | `number` | `0.25` | No | Zoom floor (25%). |
| `maxScale` | `number` | `5` | No | Zoom ceiling (500%). |
| `scaleStep` | `number` | `0.05` | No | Increment of the zoom buttons and the `+` / `-` keys. |
| `zoomLevels` | `readonly number[]` | — | No | Preset ladder, e.g. `[0.5, 1, 1.5, 2, 4]`: the zoom buttons step between presets instead of by `scaleStep`. |
| `fitWidthAtDefaultScale` | `boolean` | `true` | No | At the default scale, shrink the page to the available width. |
| `fitMode` | `'page' \| 'none' \| 'width'` | `'none'` | No | At the default scale, fill the width (`width`) or fit the whole page (`page`). Zooming away shows the page at true size. |
| `password` | `string` | — | No | Password for encrypted documents. Changing it reloads the document. |
| `pdfjsOptions` | `PdfJsDocumentOptions` | — | No | Per-instance `getDocument` overrides such as `cMapUrl`, `standardFontDataUrl` or `verbosity`. Source-related options are managed by the viewer. |
| `toolbar` | `boolean \| ToolbarConfig` | `true` | No | `false` hides the toolbar; an object sets `position`, `actions` and `hiddenWhenCompact`. |
| `compactBreakpoint` | `number` | `960` | No | Viewport width (px) below which the compact layout applies: less padding, and `hiddenWhenCompact` actions hidden outside fullscreen. |
| `keyboardShortcuts` | `boolean` | `true` | No | Keyboard shortcuts while focus is inside the viewer. |
| `printMode` | `'render' \| 'open-url'` | `'render'` | No | `render` prints through a hidden iframe, with progress and Cancel; `open-url` opens the PDF in a new tab. |
| `maxCanvasPixels` | `number` | `16777216` | No | Canvas pixel budget per page (4096 × 4096). Above it, resolution degrades gracefully instead of failing. |
| `labels` | `Partial<PdfViewerLabels>` | `English (defaultLabels)` | No | Overrides for any visible string or accessible name. Keep the object stable (e.g. at module scope). |
| `locale` | `string` | `the browser locale` | No | BCP 47 locale for number formatting, e.g. `hu-HU`. |
| `layout` | `'single' \| 'continuous'` | `'single'` | No | One page at a time or vertical scrolling through all pages. Only the visible pages ±1 are rendered; the page indicator follows the scroll position. |
| `textLayer` | `boolean` | `false` | No | Selectable, copyable text that assistive technology can read. |
| `annotationLayer` | `boolean` | `false` | No | Clickable links: internal links navigate, external links open in a new tab with `rel="noopener noreferrer"`. |
| `pageInput` | `boolean` | `false` | No | Editable page number in the toolbar. |
| `zoomReset` | `boolean` | `false` | No | The zoom label becomes a button that resets zoom. |
| `wheelZoom` | `boolean` | `false` | No | `Ctrl` / `⌘` + wheel and trackpad pinch zoom, anchored at the pointer. |
| `passwordPrompt` | `boolean` | `false` | No | Ask for the password of encrypted documents instead of reporting `PASSWORD_REQUIRED`. |
| `renderPasswordPrompt` | `(context: { incorrect: boolean; submit: (password: string) => void }) => ReactNode` | — | No | Replaces the built-in password form (with `passwordPrompt`). |
| `thumbnails` | `boolean` | `false` | No | Lazily rendered page thumbnails sidebar, hidden in compact, non-fullscreen mode. |
| `search` | `boolean` | `false` | No | Find bar with highlighting (implies `textLayer`). `Ctrl` / `⌘` + `F` focuses it, `Enter` / `Shift` + `Enter` step through matches, `Esc` clears. |
| `renderLoading` | `() => ReactNode` | — | No | Replaces the loading view. |
| `renderError` | `(error: PdfViewerError, actions: { retry: () => void }) => ReactNode` | — | No | Replaces the document and page error views. |
| `renderToolbar` | `(api: PdfViewerApi) => ReactNode` | — | No | Replaces the toolbar. Render `&lt;PdfToolbar api={api} /&gt;` inside to reuse parts. |
| `renderEmpty` | `() => ReactNode` | — | No | Replaces the view shown without a source. |
| `className` | `string` | — | No | Added to the root (`.rpv-root`). `rpv-theme-dark` and `rpv-theme-auto` select the dark presets. |
| `style` | `CSSProperties` | — | No | Inline styles on the root, e.g. CSS variables. |
| `id` | `string` | — | No | Id of the root element. |
| `aria-label` | `string` | `labels.viewer` | No | Accessible name of the viewer region. |
| `requestInit` | `RequestInit` | — | No | Extra `fetch` options for URL sources: headers, `credentials`, `mode`, … |
| `fetcher` | `(url: string, init: RequestInit) => Promise<Response>` | — | No | Replaces `fetch` entirely, e.g. with an authenticated HTTP client. |
| `getHttpErrorMessage` | `(response: Response) => Promise<string>` | — | No | Builds the `HTTP_ERROR` message. Return `undefined` for the default: an RFC 7807 `detail`, then `title`, then a JSON string body, then `Failed to fetch PDF: {status} {statusText}`. |

## Events

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `onPageChange` | `(page: number) => void` | — | No | Called whenever the page changes, controlled or not. |
| `onScaleChange` | `(scale: number) => void` | — | No | Called whenever the scale changes. |
| `onRotationChange` | `(rotation: Rotation) => void` | — | No | Called whenever the rotation changes. |
| `onFullscreenChange` | `(fullscreen: boolean) => void` | — | No | Called whenever fullscreen is entered or left. |
| `onDocumentLoad` | `(info: { numPages: number; fingerprint: string }) => void` | — | No | A document finished loading. |
| `onPageRender` | `(info: PageRenderInfo) => void` | — | No | A page render completed. |
| `onError` | `(error: PdfViewerError) => void` | — | No | Every failure except aborted loads, canceled renders and HTTP errors handled by `onHttpError`. |
| `onDownload` | `(context: { fileName: string; data: Uint8Array }) => false \| void` | — | No | Before saving; `data` holds the already-loaded bytes. Return `false` to handle the download yourself. |
| `onPrint` | `() => false \| void` | — | No | Before printing. Return `false` to handle printing yourself. |
| `onHttpError` | `(response: Response) => boolean \| void \| Promise<boolean \| void>` | — | No | Called for non-2xx responses. Return `true` when handled (e.g. after redirecting to sign-in): the viewer shows its empty state and does not call `onError`. |

## CSS classes

| Key | Class | Description |
| --- | --- | --- |
| rpv-root | `.rpv-root` | The root element. Add <code>rpv-theme-dark</code>, or <code>rpv-theme-auto</code> to follow the operating system, for the dark preset. |
| data-status | `[data-status]` | Document status. |
| data-layout | `[data-layout]` | The <code>layout</code> prop. |
| data-compact | `[data-compact]` | The viewport is narrower than <code>compactBreakpoint</code>. |
| data-fullscreen | `[data-fullscreen]` | In fullscreen, in any mode. |
| data-presentation | `[data-presentation]` | How fullscreen is presented; <code>layout</code> in controlled mode. |
| data-zoomed | `[data-zoomed]` | The scale is above <code>defaultScale</code>. |

## Source

[packages/react-pdf-viewer/src/PdfViewer.tsx](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/PdfViewer.tsx#L125)

---

# PdfViewerApi reference

> Imperative viewer API, available through ref and passed to renderToolbar. Values are a snapshot of the render that produced the object.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-api/

## Used by

- [Imperative API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api.md)

## Import

```ts
import type { PdfViewerApi } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `status` | `PdfDocumentStatus` | — | Yes | Document status. |
| `numPages` | `number` | — | Yes | Page count; `0` without a document. |
| `page` | `number` | — | Yes | Current 1-based page, clamped to the document. |
| `scale` | `number` | — | Yes | Current scale (`1` = 100%). |
| `rotation` | `Rotation` | — | Yes | Current user rotation. |
| `fullscreen` | `boolean` | — | Yes | Whether the viewer is in fullscreen. |
| `canZoomIn` | `boolean` | — | Yes | Whether `zoomIn()` would change the scale. |
| `canZoomOut` | `boolean` | — | Yes | Whether `zoomOut()` would change the scale. |
| `goToPage` | `(page: number) => void` | — | No | Go to a page (clamped). |
| `nextPage` | `() => void` | — | No | Next page. |
| `previousPage` | `() => void` | — | No | Previous page. |
| `zoomIn` | `() => void` | — | No | One step (or preset) up. |
| `zoomOut` | `() => void` | — | No | One step (or preset) down. |
| `setScale` | `(scale: number) => void` | — | No | Set the scale, clamped to `[minScale, maxScale]`. |
| `resetZoom` | `() => void` | — | No | Return to `defaultScale`. |
| `rotate` | `(direction?: 'cw' \| 'ccw') => void` | — | No | Rotate by 90°, clockwise by default. |
| `toggleFullscreen` | `() => void` | — | No | Enter or leave fullscreen. |
| `download` | `() => Promise<void>` | — | No | Save the loaded document (no second request). |
| `print` | `() => Promise<void>` | — | No | Print according to `printMode`. |
| `reload` | `() => void` | — | No | Load the source again, e.g. after an error. |
| `getDocument` | `() => PDFDocumentProxy \| null` | — | No | The loaded PDF.js document, for advanced use. Do not destroy it. |

## Source

[packages/react-pdf-viewer/src/types.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/types.ts#L153)

---

# PdfViewerError reference

> A typed viewer error. Aborted loads and canceled renders are never reported as errors.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-error/

## Used by

- [Error states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/error-states.md)

## Import

```ts
import type { PdfViewerError } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `code` | `PdfViewerErrorCode` | — | Yes | Machine-readable category. |
| `message` | `string` | — | Yes | Human-readable message; for `HTTP_ERROR` it comes from `getHttpErrorMessage`. |
| `status` | `number` | — | No | The HTTP status, for `HTTP_ERROR`. |
| `cause` | `unknown` | — | No | The underlying error, when there is one. |

## Source

[packages/react-pdf-viewer/src/types.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/types.ts#L93)

---

# PdfViewerErrorCode reference

> Machine-readable category of a PdfViewerError, for handling failures such as a missing password or an HTTP error in code.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-error-code/

## Used by

- [Error states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/error-states.md)

## Import

```ts
import type { PdfViewerErrorCode } from '@kingtech/react-pdf-viewer';
```

Signature: `type PdfViewerErrorCode = 'PDFJS_LOAD_FAILED' | 'NETWORK_ERROR' | 'HTTP_ERROR' | 'INVALID_PDF' | 'PASSWORD_REQUIRED' | 'INCORRECT_PASSWORD' | 'RENDER_FAILED' | 'DOWNLOAD_FAILED' | 'PRINT_FAILED' | 'UNKNOWN'`

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `PDFJS_LOAD_FAILED` | `'PDFJS_LOAD_FAILED'` | — | No | The `pdfjs-dist` module or its worker failed to load. |
| `NETWORK_ERROR` | `'NETWORK_ERROR'` | — | No | `fetch` rejected: offline, CORS, DNS, … |
| `HTTP_ERROR` | `'HTTP_ERROR'` | — | No | The server answered with a non-2xx status (`status` is set). |
| `INVALID_PDF` | `'INVALID_PDF'` | — | No | The bytes are not a valid PDF. |
| `PASSWORD_REQUIRED` | `'PASSWORD_REQUIRED'` | — | No | The document is encrypted and no password was supplied. |
| `INCORRECT_PASSWORD` | `'INCORRECT_PASSWORD'` | — | No | The supplied password is wrong. |
| `RENDER_FAILED` | `'RENDER_FAILED'` | — | No | A page failed to render. |
| `DOWNLOAD_FAILED` | `'DOWNLOAD_FAILED'` | — | No | Saving the document failed. |
| `PRINT_FAILED` | `'PRINT_FAILED'` | — | No | Printing failed. |
| `UNKNOWN` | `'UNKNOWN'` | — | No | Anything else. |

## Source

[packages/react-pdf-viewer/src/types.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/types.ts#L68)

---

# PdfViewerLabels reference

> Every user-visible string and accessible name. Defaults are English; override any subset through the labels prop.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-labels/

## Used by

- [Labels and locale](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/labels.md)

## Import

```ts
import type { PdfViewerLabels } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `viewer` | `string` | — | Yes | Accessible name of the viewer region. |
| `toolbar` | `string` | — | Yes | Accessible name of the toolbar. |
| `document` | `string` | — | Yes | Accessible name of the scrollable document area. |
| `zoomOut` | `string` | — | Yes | Button. |
| `zoomIn` | `string` | — | Yes | Button. |
| `zoomLevel` | `(percent: number, context: LabelContext) => string` | — | Yes | Zoom label text. |
| `resetZoom` | `string` | — | Yes | Reset-zoom button (with `zoomReset`) and menu item. |
| `previousPage` | `string` | — | Yes | Button. |
| `nextPage` | `string` | — | Yes | Button. |
| `pageIndicator` | `(page: number, total: number, context: LabelContext) => string` | — | Yes | Page label text. |
| `pageAriaLabel` | `(page: number, total: number, context: LabelContext) => string` | — | Yes | Accessible name of each page. |
| `pageInput` | `string` | — | Yes | Accessible name of the page-number input. |
| `enterFullscreen` | `string` | — | Yes | Fullscreen button outside fullscreen. |
| `exitFullscreen` | `string` | — | Yes | Fullscreen button in fullscreen. |
| `rotate` | `string` | — | Yes | Button. |
| `download` | `string` | — | Yes | Button. |
| `print` | `string` | — | Yes | Button. |
| `moreActions` | `string` | — | Yes | Overflow menu button shown when the toolbar is too narrow. |
| `loading` | `string` | — | Yes | Loading view text. |
| `retry` | `string` | — | Yes | Retry button of the error view. |
| `empty` | `string` | — | Yes | Announced when there is no document. |
| `printProgress` | `(done: number, total: number, context: LabelContext) => string` | — | Yes | Print preparation status. |
| `cancel` | `string` | — | Yes | Cancels print preparation. |
| `passwordPrompt` | `string` | — | Yes | Password form heading. |
| `passwordLabel` | `string` | — | Yes | Password field label. |
| `passwordSubmit` | `string` | — | Yes | Password submit button. |
| `passwordIncorrect` | `string` | — | Yes | Shown after a wrong password. |
| `thumbnails` | `string` | — | Yes | Accessible name of the thumbnails sidebar. |
| `search` | `string` | — | Yes | Accessible name of the search field. |
| `searchResults` | `(current: number, total: number, context: LabelContext) => string` | — | Yes | Search result status. |
| `searchPrevious` | `string` | — | Yes | Search button. |
| `searchNext` | `string` | — | Yes | Search button. |
| `searchNoResults` | `string` | — | Yes | Shown when nothing matches. |

## Source

[packages/react-pdf-viewer/src/labels.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/labels.ts#L13)

---

# PdfViewerProps reference

> Props for PdfViewer. Every piece of state supports the value / defaultValue / onChange trio.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-props/

## Used by

- [Page navigation](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/page-navigation.md)
- [Zoom](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/zoom.md)
- [Rotation](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/rotation.md)
- [Download](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/download.md)
- [Print](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/print.md)
- [Fit modes](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/fit-modes.md)
- [Fullscreen](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/fullscreen.md)
- [Continuous layout](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/continuous-layout.md)
- [Thumbnails](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/thumbnails.md)
- [Loading states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/loading-states.md)
- [Page number input](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/page-number-input.md)
- [Links](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/links.md)
- [Text layer](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/text-layer.md)
- [Search](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/search.md)
- [Password protection](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/password-protection.md)
- [Rendering](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/rendering.md)

## Import

```ts
import type { PdfViewerProps } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `source` | `PdfSource \| null` | — | Yes | The document: a URL string or `URL`, or `ArrayBuffer` / `Uint8Array` / `Blob` / `File`. `null`, `undefined` and `''` show the empty state. |
| `fileName` | `string` | `derived from the source` | No | Download file name. Default: a `File`’s name, the URL’s last `.pdf` segment, else `document.pdf`. |
| `page` | `number` | — | No | Controlled 1-based page. Out-of-range values are clamped and reported through `onPageChange`. |
| `defaultPage` | `number` | `1` | No | Initial page while uncontrolled, and the page every new document opens on. |
| `onPageChange` | `(page: number) => void` | — | No | Called whenever the page changes, controlled or not. |
| `scale` | `number` | — | No | Controlled scale: `1` = 100%, one PDF point per CSS pixel. |
| `defaultScale` | `number` | `1` | No | Initial scale while uncontrolled; also the reset-zoom target and the scale at which fitting applies. |
| `onScaleChange` | `(scale: number) => void` | — | No | Called whenever the scale changes. |
| `rotation` | `Rotation` | — | No | Controlled clockwise rotation, added to each page’s intrinsic rotation. |
| `defaultRotation` | `Rotation` | `0` | No | Initial rotation while uncontrolled. |
| `onRotationChange` | `(rotation: Rotation) => void` | — | No | Called whenever the rotation changes. |
| `fullscreen` | `boolean` | — | No | Controlled fullscreen state. Passing it selects `fullscreenMode="controlled"` unless a mode is set. |
| `defaultFullscreen` | `boolean` | `false` | No | Initial fullscreen state while uncontrolled. |
| `onFullscreenChange` | `(fullscreen: boolean) => void` | — | No | Called whenever fullscreen is entered or left. |
| `fullscreenMode` | `FullscreenMode` | `'controlled' when fullscreen is passed, else 'native'` | No | `controlled`: layout only, the parent presents fullscreen (e.g. in its own dialog). `native`: the browser Fullscreen API on the viewer root, falling back to `overlay`. `overlay`: a fixed full-viewport layer with a focus trap; `Esc` closes it. |
| `minScale` | `number` | `0.25` | No | Zoom floor (25%). |
| `maxScale` | `number` | `5` | No | Zoom ceiling (500%). |
| `scaleStep` | `number` | `0.05` | No | Increment of the zoom buttons and the `+` / `-` keys. |
| `zoomLevels` | `readonly number[]` | — | No | Preset ladder, e.g. `[0.5, 1, 1.5, 2, 4]`: the zoom buttons step between presets instead of by `scaleStep`. |
| `fitWidthAtDefaultScale` | `boolean` | `true` | No | At the default scale, shrink the page to the available width. |
| `fitMode` | `'page' \| 'none' \| 'width'` | `'none'` | No | At the default scale, fill the width (`width`) or fit the whole page (`page`). Zooming away shows the page at true size. |
| `password` | `string` | — | No | Password for encrypted documents. Changing it reloads the document. |
| `pdfjsOptions` | `PdfJsDocumentOptions` | — | No | Per-instance `getDocument` overrides such as `cMapUrl`, `standardFontDataUrl` or `verbosity`. Source-related options are managed by the viewer. |
| `toolbar` | `boolean \| ToolbarConfig` | `true` | No | `false` hides the toolbar; an object sets `position`, `actions` and `hiddenWhenCompact`. |
| `compactBreakpoint` | `number` | `960` | No | Viewport width (px) below which the compact layout applies: less padding, and `hiddenWhenCompact` actions hidden outside fullscreen. |
| `keyboardShortcuts` | `boolean` | `true` | No | Keyboard shortcuts while focus is inside the viewer. |
| `printMode` | `'render' \| 'open-url'` | `'render'` | No | `render` prints through a hidden iframe, with progress and Cancel; `open-url` opens the PDF in a new tab. |
| `maxCanvasPixels` | `number` | `16777216` | No | Canvas pixel budget per page (4096 × 4096). Above it, resolution degrades gracefully instead of failing. |
| `labels` | `Partial<PdfViewerLabels>` | `English (defaultLabels)` | No | Overrides for any visible string or accessible name. Keep the object stable (e.g. at module scope). |
| `locale` | `string` | `the browser locale` | No | BCP 47 locale for number formatting, e.g. `hu-HU`. |
| `layout` | `'single' \| 'continuous'` | `'single'` | No | One page at a time or vertical scrolling through all pages. Only the visible pages ±1 are rendered; the page indicator follows the scroll position. |
| `textLayer` | `boolean` | `false` | No | Selectable, copyable text that assistive technology can read. |
| `annotationLayer` | `boolean` | `false` | No | Clickable links: internal links navigate, external links open in a new tab with `rel="noopener noreferrer"`. |
| `pageInput` | `boolean` | `false` | No | Editable page number in the toolbar. |
| `zoomReset` | `boolean` | `false` | No | The zoom label becomes a button that resets zoom. |
| `wheelZoom` | `boolean` | `false` | No | `Ctrl` / `⌘` + wheel and trackpad pinch zoom, anchored at the pointer. |
| `passwordPrompt` | `boolean` | `false` | No | Ask for the password of encrypted documents instead of reporting `PASSWORD_REQUIRED`. |
| `renderPasswordPrompt` | `(context: { incorrect: boolean; submit: (password: string) => void }) => ReactNode` | — | No | Replaces the built-in password form (with `passwordPrompt`). |
| `thumbnails` | `boolean` | `false` | No | Lazily rendered page thumbnails sidebar, hidden in compact, non-fullscreen mode. |
| `search` | `boolean` | `false` | No | Find bar with highlighting (implies `textLayer`). `Ctrl` / `⌘` + `F` focuses it, `Enter` / `Shift` + `Enter` step through matches, `Esc` clears. |
| `renderLoading` | `() => ReactNode` | — | No | Replaces the loading view. |
| `renderError` | `(error: PdfViewerError, actions: { retry: () => void }) => ReactNode` | — | No | Replaces the document and page error views. |
| `renderToolbar` | `(api: PdfViewerApi) => ReactNode` | — | No | Replaces the toolbar. Render `&lt;PdfToolbar api={api} /&gt;` inside to reuse parts. |
| `renderEmpty` | `() => ReactNode` | — | No | Replaces the view shown without a source. |
| `onDocumentLoad` | `(info: { numPages: number; fingerprint: string }) => void` | — | No | A document finished loading. |
| `onPageRender` | `(info: PageRenderInfo) => void` | — | No | A page render completed. |
| `onError` | `(error: PdfViewerError) => void` | — | No | Every failure except aborted loads, canceled renders and HTTP errors handled by `onHttpError`. |
| `onDownload` | `(context: { fileName: string; data: Uint8Array }) => false \| void` | — | No | Before saving; `data` holds the already-loaded bytes. Return `false` to handle the download yourself. |
| `onPrint` | `() => false \| void` | — | No | Before printing. Return `false` to handle printing yourself. |
| `className` | `string` | — | No | Added to the root (`.rpv-root`). `rpv-theme-dark` and `rpv-theme-auto` select the dark presets. |
| `style` | `CSSProperties` | — | No | Inline styles on the root, e.g. CSS variables. |
| `id` | `string` | — | No | Id of the root element. |
| `aria-label` | `string` | `labels.viewer` | No | Accessible name of the viewer region. |
| `requestInit` | `RequestInit` | — | No | Extra `fetch` options for URL sources: headers, `credentials`, `mode`, … |
| `fetcher` | `(url: string, init: RequestInit) => Promise<Response>` | — | No | Replaces `fetch` entirely, e.g. with an authenticated HTTP client. |
| `onHttpError` | `(response: Response) => boolean \| void \| Promise<boolean \| void>` | — | No | Called for non-2xx responses. Return `true` when handled (e.g. after redirecting to sign-in): the viewer shows its empty state and does not call `onError`. |
| `getHttpErrorMessage` | `(response: Response) => Promise<string>` | — | No | Builds the `HTTP_ERROR` message. Return `undefined` for the default: an RFC 7807 `detail`, then `title`, then a JSON string body, then `Failed to fetch PDF: {status} {statusText}`. |

## Source

[packages/react-pdf-viewer/src/types.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/types.ts#L190)

---

# Rotation reference

> Clockwise rotation in degrees, relative to the page's natural orientation.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/rotation/

## Used by

- [Rotation](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/rotation.md)

## Import

```ts
import type { Rotation } from '@kingtech/react-pdf-viewer';
```

Signature: `type Rotation = 0 | 90 | 180 | 270`

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `0` | `0` | — | No | Upright (the page's natural orientation). |
| `90` | `90` | — | No | A quarter turn clockwise. |
| `180` | `180` | — | No | Upside down. |
| `270` | `270` | — | No | A quarter turn counter-clockwise. |

## Source

[packages/react-pdf-viewer/src/types.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/types.ts#L18)

---

# rpv-theme-auto reference

> Automatic preset: add className="rpv-theme-auto" to apply the dark values only when the operating system prefers a dark color scheme.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/rpv-theme-auto/

## Used by

- [Theming](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/theming.md)

## Import

```ts
import '@kingtech/react-pdf-viewer/styles.css';
```

## Settings

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `--rpv-accent` | `CSS value` | `#90caf9` | No | Accent: focus outlines outside the toolbar, progress, the current thumbnail (dark value). |
| `--rpv-error-bg` | `CSS value` | `#160b0b` | No | Error view background (dark value). |
| `--rpv-error-fg` | `CSS value` | `#f4c7c7` | No | Error view text (dark value). |
| `--rpv-error-icon` | `CSS value` | `#f44336` | No | Error view icon (dark value). |
| `--rpv-focus-ring` | `CSS value` | `2px solid #90caf9` | No | Focus outline of toolbar controls (dark value). |
| `--rpv-fullscreen-bg` | `CSS value` | `#121212` | No | Fullscreen background (dark value). |
| `--rpv-menu-bg` | `CSS value` | `#2b363c` | No | Menu surface (dark value). |
| `--rpv-menu-border` | `CSS value` | `rgb(255 255 255 / 0.08)` | No | Menu border color (dark value). |
| `--rpv-menu-fg` | `CSS value` | `#eceff1` | No | Menu item text (dark value). |
| `--rpv-menu-hover-bg` | `CSS value` | `rgb(255 255 255 / 0.08)` | No | Hovered and focused item (dark value). |
| `--rpv-menu-icon-fg` | `CSS value` | `#b0bec5` | No | Menu item icons (dark value). |
| `--rpv-muted-fg` | `CSS value` | `rgba(255, 255, 255, 0.7)` | No | Secondary text (dark value). |
| `--rpv-page-border` | `CSS value` | `1px solid #37474f` | No | Page border (dark value). |
| `--rpv-page-shadow` | `CSS value` | `0 2px 12px rgba(0, 0, 0, 0.6)` | No | Page shadow (dark value). |
| `--rpv-text-button-fg` | `CSS value` | `#90caf9` | No | Retry and Cancel text buttons (dark value). |
| `--rpv-toolbar-bg` | `CSS value` | `#263238` | No | Toolbar background (dark value). |
| `--rpv-toolbar-fg` | `CSS value` | `#eceff1` | No | Toolbar icons and text (dark value). |

## Source

[packages/react-pdf-viewer/src/styles/pdf-viewer.css](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/styles/pdf-viewer.css#L278)

---

# rpv-theme-dark reference

> Dark preset: add className="rpv-theme-dark" to the viewer to apply these values.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/rpv-theme-dark/

## Used by

- [Theming](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/theming.md)

## Import

```ts
import '@kingtech/react-pdf-viewer/styles.css';
```

## Settings

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `--rpv-accent` | `CSS value` | `#90caf9` | No | Accent: focus outlines outside the toolbar, progress, the current thumbnail (dark value). |
| `--rpv-error-bg` | `CSS value` | `#160b0b` | No | Error view background (dark value). |
| `--rpv-error-fg` | `CSS value` | `#f4c7c7` | No | Error view text (dark value). |
| `--rpv-error-icon` | `CSS value` | `#f44336` | No | Error view icon (dark value). |
| `--rpv-focus-ring` | `CSS value` | `2px solid #90caf9` | No | Focus outline of toolbar controls (dark value). |
| `--rpv-fullscreen-bg` | `CSS value` | `#121212` | No | Fullscreen background (dark value). |
| `--rpv-menu-bg` | `CSS value` | `#2b363c` | No | Menu surface (dark value). |
| `--rpv-menu-border` | `CSS value` | `rgb(255 255 255 / 0.08)` | No | Menu border color (dark value). |
| `--rpv-menu-fg` | `CSS value` | `#eceff1` | No | Menu item text (dark value). |
| `--rpv-menu-hover-bg` | `CSS value` | `rgb(255 255 255 / 0.08)` | No | Hovered and focused item (dark value). |
| `--rpv-menu-icon-fg` | `CSS value` | `#b0bec5` | No | Menu item icons (dark value). |
| `--rpv-muted-fg` | `CSS value` | `rgba(255, 255, 255, 0.7)` | No | Secondary text (dark value). |
| `--rpv-page-border` | `CSS value` | `1px solid #37474f` | No | Page border (dark value). |
| `--rpv-page-shadow` | `CSS value` | `0 2px 12px rgba(0, 0, 0, 0.6)` | No | Page shadow (dark value). |
| `--rpv-text-button-fg` | `CSS value` | `#90caf9` | No | Retry and Cancel text buttons (dark value). |
| `--rpv-toolbar-bg` | `CSS value` | `#263238` | No | Toolbar background (dark value). |
| `--rpv-toolbar-fg` | `CSS value` | `#eceff1` | No | Toolbar icons and text (dark value). |

## Source

[packages/react-pdf-viewer/src/styles/pdf-viewer.css](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/styles/pdf-viewer.css#L268)

---

# SetControllableState reference

> Setter returned by useControllableState; accepts a value or an updater function.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/set-controllable-state/

## Used by

- [Controlled state](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/controlled-state.md)

## Import

```ts
import type { SetControllableState } from '@kingtech/react-pdf-viewer';
```

Signature: `type SetControllableState<T> = (next: T | (previous: T) => T) => void`

## Source

[packages/react-pdf-viewer/src/hooks/useControllableState.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/hooks/useControllableState.ts#L18)

---

# ToolbarAction reference

> Identifies a toolbar control, for choosing which controls the toolbar shows and in which order through ToolbarConfig.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/toolbar-action/

## Used by

- [Toolbar](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/toolbar.md)

## Import

```ts
import type { ToolbarAction } from '@kingtech/react-pdf-viewer';
```

Signature: `type ToolbarAction = 'zoomOut' | 'zoomLevel' | 'zoomIn' | 'previousPage' | 'pageIndicator' | 'nextPage' | 'fullscreen' | 'rotate' | 'download' | 'print'`

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `zoomOut` | `'zoomOut'` | — | No | Zoom out button. Zoom group. |
| `zoomLevel` | `'zoomLevel'` | — | No | Zoom percentage; a reset button with `zoomReset`. Zoom group. |
| `zoomIn` | `'zoomIn'` | — | No | Zoom in button. Zoom group. |
| `previousPage` | `'previousPage'` | — | No | Previous page button. Page group. |
| `pageIndicator` | `'pageIndicator'` | — | No | Current page and total; an input with `pageInput`. Page group. |
| `nextPage` | `'nextPage'` | — | No | Next page button. Page group. |
| `fullscreen` | `'fullscreen'` | — | No | Enter / exit fullscreen. Actions group. |
| `rotate` | `'rotate'` | — | No | Rotate 90° clockwise. Actions group. |
| `download` | `'download'` | — | No | Save the loaded document. Actions group. |
| `print` | `'print'` | — | No | Print according to `printMode`. Actions group. |

## Source

[packages/react-pdf-viewer/src/types.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/types.ts#L44)

---

# ToolbarConfig reference

> Toolbar customization: where the toolbar sits, which controls it shows and in which order, and which it hides on narrow screens.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/toolbar-config/

## Used by

- [Toolbar](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/toolbar.md)

## Import

```ts
import type { ToolbarConfig } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `position` | `'top' \| 'bottom'` | `'bottom'` | No | Toolbar placement (below the page by default). |
| `actions` | `readonly ToolbarAction[]` | `all actions` | No | Actions to show. Their order within each group is fixed. |
| `hiddenWhenCompact` | `readonly ToolbarAction[]` | `['rotate', 'print']` | No | Actions hidden in compact, non-fullscreen mode. |

## Source

[packages/react-pdf-viewer/src/types.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/types.ts#L136)

---

# useControllableState reference

> The value / defaultValue / onValueChange pattern as a hook: the one behind every controlled prop of the viewer.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-controllable-state/

Internal state is used while `value` is `undefined`; `onChange` fires whenever the value changes. The setter is stable. Values must not be functions (a function argument is treated as an updater).

## Used by

- [Controlled state](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/controlled-state.md)

## Import

```ts
import { useControllableState } from '@kingtech/react-pdf-viewer';
```

Signature: `useControllableState(options: UseControllableStateOptions<T>): readonly [T, SetControllableState<T>]` → `readonly [T, SetControllableState<T>]`

## Options

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `value` | `T` | — | Yes | The controlled value; `undefined` = uncontrolled. |
| `defaultValue` | `T \| () => T` | — | Yes | Initial value while uncontrolled. |
| `onChange` | `(value: T) => void` | — | No | Called with every change, controlled or not. |
| `name` | `string` | — | No | Prop name used in development warnings. |

## Source

[packages/react-pdf-viewer/src/hooks/useControllableState.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/hooks/useControllableState.ts#L25)

---

# UseControllableStateOptions reference

> Options for useControllableState: the controlled value, the initial value while uncontrolled, and the change callback.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-controllable-state-options/

## Used by

- [Controlled state](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/controlled-state.md)

## Import

```ts
import type { UseControllableStateOptions } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `value` | `T` | — | Yes | The controlled value; `undefined` = uncontrolled. |
| `defaultValue` | `T \| () => T` | — | Yes | Initial value while uncontrolled. |
| `onChange` | `(value: T) => void` | — | No | Called with every change, controlled or not. |
| `name` | `string` | — | No | Prop name used in development warnings. |

## Source

[packages/react-pdf-viewer/src/hooks/useControllableState.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/hooks/useControllableState.ts#L6)

---

# usePdfDocument reference

> Loads a PDF document. Every source change aborts the previous fetch, discards its result and destroys the previous document; unmounting does the same.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-pdf-document/

## Used by

- [Headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless.md)

## Import

```ts
import { usePdfDocument } from '@kingtech/react-pdf-viewer';
```

Signature: `usePdfDocument(source: PdfSource | null, options?: UsePdfDocumentOptions): UsePdfDocumentResult` → `UsePdfDocumentResult`

## Options

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `password` | `string` | — | No | Password for encrypted documents. Changing it reloads the document. |
| `pdfjsOptions` | `PdfJsDocumentOptions` | — | No | Per-instance `getDocument` overrides. |
| `onLoad` | `(document: PDFDocumentProxy) => void` | — | No | The document is ready. |
| `onError` | `(error: PdfViewerError) => void` | — | No | Every load failure except aborts and handled HTTP errors. |
| `requestInit` | `RequestInit` | — | No | As on `PdfViewer`. |
| `fetcher` | `(url: string, init: RequestInit) => Promise<Response>` | — | No | As on `PdfViewer`. |
| `onHttpError` | `(response: Response) => boolean \| void \| Promise<boolean \| void>` | — | No | As on `PdfViewer`. |
| `getHttpErrorMessage` | `(response: Response) => Promise<string>` | — | No | As on `PdfViewer`. |

## Source

[packages/react-pdf-viewer/src/hooks/usePdfDocument.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/hooks/usePdfDocument.ts#L54)

---

# UsePdfDocumentOptions reference

> Options for usePdfDocument. Changes apply on the next load, except password.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-pdf-document-options/

## Used by

- [Headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless.md)

## Import

```ts
import type { UsePdfDocumentOptions } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `password` | `string` | — | No | Password for encrypted documents. Changing it reloads the document. |
| `pdfjsOptions` | `PdfJsDocumentOptions` | — | No | Per-instance `getDocument` overrides. |
| `onLoad` | `(document: PDFDocumentProxy) => void` | — | No | The document is ready. |
| `onError` | `(error: PdfViewerError) => void` | — | No | Every load failure except aborts and handled HTTP errors. |
| `requestInit` | `RequestInit` | — | No | As on `PdfViewer`. |
| `fetcher` | `(url: string, init: RequestInit) => Promise<Response>` | — | No | As on `PdfViewer`. |
| `onHttpError` | `(response: Response) => boolean \| void \| Promise<boolean \| void>` | — | No | As on `PdfViewer`. |
| `getHttpErrorMessage` | `(response: Response) => Promise<string>` | — | No | As on `PdfViewer`. |

## Source

[packages/react-pdf-viewer/src/hooks/usePdfDocument.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/hooks/usePdfDocument.ts#L16)

---

# UsePdfDocumentResult reference

> State returned by usePdfDocument: the load status, the document once ready, its page count, the error and a reload function.

Reference · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/use-pdf-document-result/

## Used by

- [Headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless.md)

## Import

```ts
import type { UsePdfDocumentResult } from '@kingtech/react-pdf-viewer';
```

## Properties

| Name | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `status` | `PdfDocumentStatus` | — | Yes | `idle` without a source (or after a handled HTTP error). |
| `document` | `PDFDocumentProxy \| null` | — | Yes | The document while `ready`. Never a destroyed document. |
| `numPages` | `number` | — | Yes | Page count; `0` without a document. |
| `error` | `PdfViewerError \| null` | — | Yes | The failure while `error`. |
| `reload` | `() => void` | — | Yes | Load the current source again. |

## Source

[packages/react-pdf-viewer/src/hooks/usePdfDocument.ts](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/packages/react-pdf-viewer/src/hooks/usePdfDocument.ts#L28)

---

# How to customize

> Choose the right way to change how the viewer looks and behaves, from a CSS variable to a completely custom interface.

Customization · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/

React PDF Viewer is customized in layers. Start with the lightest mechanism that achieves your goal; each step down gives more control and more responsibility.

## Ways to customize

| You want to…                                         | Use                                          | Where                                                                         |
| ---------------------------------------------------- | -------------------------------------------- | ----------------------------------------------------------------------------- |
| Match your brand colors, radius, spacing and fonts   | CSS variables                                | [Theming](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/theming/)                           |
| Support dark mode                                    | `rpv-theme-dark` or `rpv-theme-auto` classes | [Theming](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/theming/#dark-mode)                 |
| Style a state, such as loading or fullscreen         | `data-*` attributes on the root              | [Theming](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/theming/#state-based-styling)       |
| Change or translate texts and accessible names       | `labels` and `locale`                        | [Labels and locale](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/labels/)                                |
| Choose, place or hide toolbar controls               | `toolbar` config                             | [Toolbar](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/toolbar/)                                         |
| Replace the loading, empty, error or password views  | `render*` props                              | [Overriding structure](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/overriding-structure/) |
| Replace the toolbar, or mix it with your own buttons | `renderToolbar` and `PdfToolbar`             | [Custom toolbar](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar/)                           |
| Build a completely different interface               | `usePdfDocument` and `PdfPageCanvas`         | [Headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/)                                       |

## How the styles are built

The stylesheet is plain CSS in a cascade layer named `rpv`, with every class prefixed `rpv-`. Because it is layered, **any unlayered CSS in your app wins without `!important`**. Every color and size reads a CSS variable with a fallback, so setting a variable on the viewer or on any ancestor changes it.

```css
.reports {
  --rpv-toolbar-bg: #1f2937;
  --rpv-accent: #7c3aed;
}
```

## Related

- [Theming](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/theming/)
- [Overriding structure](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/overriding-structure/)
- [Recipes](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/recipes/)
- [CSS variables reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/css-variables/)

---

# Theming

> Match the viewer to your design with CSS variables, dark and automatic presets, and state-based styling.

Customization · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/theming/

## Set CSS variables

Every color, size and spacing value in the viewer is a CSS variable. Set them on the viewer (`style` or `className`) or on any ancestor, so a whole section of your app can share one theme.

*Example: A custom theme from a few variables* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';
import type { CSSProperties } from 'react';

const theme = {
  '--rpv-toolbar-bg': '#0f172a',
  '--rpv-toolbar-radius': '14px',
  '--rpv-accent': '#6366f1',
  '--rpv-page-radius': '10px',
  '--rpv-page-border': '1px solid #c7d2fe',
  '--rpv-page-shadow': '0 16px 40px rgba(79, 70, 229, 0.18)',
} as CSSProperties;

export default function Demo() {
  return (
    <div style={theme}>
      <PdfViewer source="/samples/letter-3pages.pdf" pageInput />
    </div>
  );
}
```

The most useful variables:

| Variable                                                                             | Controls                                        |
| ------------------------------------------------------------------------------------ | ----------------------------------------------- |
| `--rpv-toolbar-bg`, `--rpv-toolbar-fg`                                               | Toolbar colors                                  |
| `--rpv-accent`                                                                       | Focus outlines, the current thumbnail, progress |
| `--rpv-toolbar-radius`, `--rpv-button-size`, `--rpv-icon-size`                       | Toolbar shape and sizes                         |
| `--rpv-page-bg`, `--rpv-page-border`, `--rpv-page-radius`, `--rpv-page-shadow`       | The page frame                                  |
| `--rpv-viewport-max-width`, `--rpv-viewport-min-height`, `--rpv-viewport-max-height` | The document area                               |
| `--rpv-font-family`                                                                  | Viewer text (inherits by default)               |

All 54 variables, with their default values, are generated from the stylesheet in the [CSS variables reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/css-variables/).

## Dark mode

`className="rpv-theme-dark"` applies the dark preset. `className="rpv-theme-auto"` applies it only when the operating system prefers a dark color scheme.

*Example: The dark preset* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export default function Demo() {
  return (
    <div style={{ padding: 16, background: '#121212', borderRadius: 12 }}>
      <PdfViewer source="/samples/multipage.pdf" className="rpv-theme-dark" search thumbnails />
    </div>
  );
}
```

The values each preset sets are in the [`rpv-theme-dark`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/rpv-theme-dark/) and [`rpv-theme-auto`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/rpv-theme-auto/) references. If your app has its own dark-mode switch, toggle the class from your theme state, or set the variables yourself inside your dark-mode selector.

## State-based styling

The root element (`.rpv-root`) reflects the viewer's state as attributes, so CSS can react to it without JavaScript:

| Attribute           | Present or value                      |
| ------------------- | ------------------------------------- |
| `data-status`       | `idle`, `loading`, `ready` or `error` |
| `data-layout`       | `single` or `continuous`              |
| `data-compact`      | below `compactBreakpoint`             |
| `data-fullscreen`   | in fullscreen, in any mode            |
| `data-presentation` | `layout`, `native` or `overlay`       |
| `data-zoomed`       | while zoomed above `defaultScale`     |

*Example: A red toolbar while in the error state* — the source of the live demo on this page.

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

const css = `
  .status-aware[data-status='error'] {
    --rpv-toolbar-bg: #991b1b;
  }
  .status-aware[data-status='loading'] {
    --rpv-toolbar-bg: #475569;
  }
`;

export default function Demo() {
  return (
    <>
      <style>{css}</style>
      <PdfViewer className="status-aware" source="/samples/does-not-exist.pdf" />
    </>
  );
}
```

The attributes are generated from the component source in the [`PdfViewer` reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer/#css-classes).

## Fonts

The viewer inherits your page's font. Set `--rpv-font-family` to use a different one for viewer text. Page content always uses the fonts embedded in the PDF.

## Related

- [How to customize](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/)
- [Recipes](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/recipes/)
- [Accessibility](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/accessibility/): keep contrast when you change colors
- [CSS variables reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/css-variables/)

---

# Overriding structure

> Replace the viewer's loading, empty, error, password and toolbar views with your own components.

Customization · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/overriding-structure/

Render props replace whole parts of the viewer while it keeps doing the work: loading, error handling, retries and state.

## Replace a view

| Prop                                          | Replaces                                  | Receives                                                                             |
| --------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------ |
| `renderLoading()`                             | The loading spinner and message           | nothing                                                                              |
| `renderEmpty()`                               | The view shown without a source           | nothing                                                                              |
| `renderError(error, { retry })`               | The document and page error views         | the [`PdfViewerError`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-error/) and a retry function |
| `renderPasswordPrompt({ incorrect, submit })` | The password form (with `passwordPrompt`) | whether the last password was wrong, and a submit function                           |
| `renderToolbar(api)`                          | The toolbar                               | the [viewer API](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/imperative-api/)                                  |

```tsx
<PdfViewer
  source={url}
  renderLoading={() => <Skeleton height={640} />}
  renderEmpty={() => <EmptyState title="Select a document" />}
  renderError={(error, { retry }) => <ErrorPanel message={error.message} onRetry={retry} />}
/>
```

Each capability page shows its slot live: [Loading states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/loading-states/), [Document sources](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/document-sources/#customization), [Error states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/error-states/#customization), [Password protection](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/password-protection/#customization) and [Custom toolbar](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar/).

## Keep what the default views provide

The built-in views are accessible by default. When you replace one, keep these behaviors:

- **Loading:** give the element `role="status"` so the change is announced.
- **Errors:** use `role="alert"` and keep a way to retry.
- **Password:** label the field, mark it `aria-invalid` after a wrong password and announce the error.
- **Toolbar:** label every control and keep disabled states in sync; see the note on [Custom toolbar](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar/#advanced--escape-hatch).

## Beyond render props

When the viewer's overall structure is wrong for you, not just one view, build the interface yourself with the [headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) pieces.

## Related

- [How to customize](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/)
- [Custom toolbar](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar/)
- [Headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/)
- [`PdfViewer` reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer/)

---

# Recipes

> Copy-paste solutions for common customizations, from full-height viewers to host styles that leak into the viewer.

Customization · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/recipes/

## Fill the available height

By default the document area has a minimum and maximum height. To make the viewer fill a container (a panel, a dialog, a page below a header), lift those limits and give the viewer a height:

```css
.viewer-panel {
  display: flex;
  flex-direction: column;
  height: calc(100dvh - 64px);
}

.viewer-panel .rpv-root {
  --rpv-viewport-max-width: 100%;
  --rpv-viewport-max-height: 100%;
  --rpv-viewport-min-height: 0px;
  --rpv-viewport-min-height-compact: 0px;
  flex: 1;
  min-height: 0;
}
```

## Keep host styles out of the viewer

The viewer's CSS is in a cascade layer, so unlayered rules in your app always win, including rules that target bare elements such as `button { … }` or `input[type='text'] { width: 100% }`. Scope those rules so they skip the viewer:

```css
:where(button):not(.rpv-root *) {
  background: var(--brand-surface);
}
```

The page-number input is guarded against common `input` rules, but broad element selectors are best scoped.

## Match a design system's toolbar

Set the toolbar variables from your design tokens once, on a shared ancestor:

```css
:root {
  --rpv-toolbar-bg: var(--color-surface-inverse);
  --rpv-toolbar-fg: var(--color-text-inverse);
  --rpv-toolbar-radius: var(--radius-lg);
  --rpv-accent: var(--color-focus);
  --rpv-font-family: var(--font-sans);
}
```

## A toolbar-less, embedded preview

For small previews in lists or cards, hide the toolbar and shortcuts and fit the page:

```tsx
<PdfViewer
  source={url}
  toolbar={false}
  keyboardShortcuts={false}
  fitMode="page"
  style={
    { '--rpv-viewport-min-height': '240px', '--rpv-viewport-max-height': '240px' } as CSSProperties
  }
  aria-label={`Preview of ${name}`}
/>
```

For many previews at once, [`PdfPageCanvas`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/) is lighter.

## Related

- [Theming](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/theming/)
- [Overriding structure](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/overriding-structure/)
- [Performance](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/performance/)

---

# Best practices

> Habits that keep a React PDF Viewer integration fast, correct and maintainable in production.

Guides · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/best-practices/

## Self-host PDF.js for production

The CDN default is convenient in development, but production apps should serve the worker and assets from their own origin: it removes a third-party dependency, speeds up loading, and fits a strict Content Security Policy. Call `configurePdfJs` once in your entry module. See [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/).

## Keep sources and objects stable

- **Binary sources** (`File`, `Blob`, `ArrayBuffer`, `Uint8Array`) compare by identity. Keep them in state or memoise them; creating one during render reloads the document every time.
- **`labels`** are merged per object identity. Define them at module scope or with `useMemo`.
- **`toolbar` config and `zoomLevels`** are cheap, but module-scope constants make intent clear and avoid needless work.

```tsx
// ✅ Stable
const LABELS = { zoomIn: 'Enlarge' };

function Viewer({ file }: { file: File }) {
  return <PdfViewer source={file} labels={LABELS} />;
}
```

> **Warning: Anti-pattern: a new source on every render**
>
> ```tsx
> // ❌ Wrong: a new Blob every render reloads the document continuously
> <PdfViewer source={new Blob([bytes], { type: 'application/pdf' })} />
> ```

## Control only the state you use

Leave state uncontrolled unless your app reads or sets it. Controlling everything means handling every change yourself; controlling only `page` (for example to sync it with the URL) keeps the rest effortless. See [Controlled state](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/controlled-state/).

## Remount deliberately

Changing `source` already aborts and reloads correctly. Use a `key` only when you need a clean slate, such as after changing `fetcher` or `requestInit`, which apply to the next load.

## Handle errors once, visibly

The default error view already offers Retry. Use `onError` for logging, `renderError` for wording that fits your product, and `onHttpError` for authentication flows that should not look like errors. See [Error states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/error-states/).

## Give each viewer a name

When a page shows more than one viewer, give each an `aria-label` (“Invoice preview”, “Contract”) so screen reader users can tell the regions apart. See [Accessibility](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/accessibility/).

## Prefer props over CSS overrides

Style through CSS variables and props rather than targeting internal `rpv-` class names: variables are the supported contract; internal structure can change between versions. See [Theming](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/theming/).

## Related

- [Performance](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/performance/)
- [Security](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/security/)
- [FAQ](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/faq/)

---

# Performance

> Keep large documents, many viewers and slow devices fast with the right layout, loading and rendering settings.

Guides · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/performance/

The package itself is small and has no runtime dependencies; most of the cost of showing a PDF is PDF.js parsing the file and painting pages. These techniques keep that cost where users don't notice it.

## Load PDF.js only when needed

PDF.js is imported lazily the first time a document opens, so pages without a viewer never download it. To also defer the viewer's own code, load the component lazily:

```tsx
const PdfViewer = lazy(() =>
  import('@kingtech/react-pdf-viewer').then((module) => ({ default: module.PdfViewer })),
);

<Suspense fallback={<Spinner />}>
  <PdfViewer source={url} />
</Suspense>;
```

## Serve assets from your origin

Self-hosting the worker, CMaps and fonts avoids an extra connection to a CDN and lets your cache headers apply. Hashed worker file names from your bundler can be cached for a long time. See [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/).

## Use continuous layout for long documents

In `layout="continuous"`, every page reserves its space but only the visible pages and one page on each side are rendered. Thumbnails render as they scroll into view. Both keep memory flat regardless of page count. See [Continuous layout](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/continuous-layout/).

## Mind the pixel budget on mobile

Canvas memory grows with zoom and screen density. `maxCanvasPixels` (16,777,216 by default, the iOS Safari limit) caps each page's canvas; on memory-constrained devices a lower budget trades sharpness for stability. See [Rendering](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/rendering/).

## Turn on only what you use

The text layer, links and search add work per page. Leave them off when users only need to look at documents.

## Many small previews

For grids of previews, `PdfPageCanvas` with a small `fit` box is far lighter than many full viewers: load each document once with `usePdfDocument` and render only its first page. See [Headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/).

## Measure

`onPageRender` reports `durationMs` for every render; debounce it and send it to your monitoring to see real-world render times. See [Events](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/events/).

## Related

- [Rendering](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/rendering/)
- [Continuous layout](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/continuous-layout/)
- [Best practices](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/best-practices/)

---

# Testing

> Test apps that use React PDF Viewer with unit tests for your logic and browser tests for real rendering.

Guides · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/testing/

## Split what you test

- **Unit tests** (Vitest or Jest with jsdom) are good for your own logic around the viewer: which source you pass, how you react to `onPageChange` or `onError`. jsdom has no canvas, so they cannot check rendering.
- **Browser tests** (Playwright, Cypress) load real documents in a real browser and are the right place to check that pages render and controls work.

## Unit tests: test your props and callbacks

Mock the component at the module boundary and assert what your code passes to it:

```tsx
vi.mock('@kingtech/react-pdf-viewer', () => ({
  PdfViewer: (props: { source: unknown; onPageChange?: (page: number) => void }) => (
    <button onClick={() => props.onPageChange?.(2)}>viewer for {String(props.source)}</button>
  ),
}));

it('opens the selected report and tracks the page', async () => {
  render(<ReportScreen reportId="42" />);
  expect(screen.getByRole('button')).toHaveTextContent('viewer for /api/reports/42.pdf');
  await userEvent.click(screen.getByRole('button'));
  expect(screen.getByText('Page 2')).toBeInTheDocument();
});
```

## Browser tests: wait for the canvas

A page has finished rendering when its canvas is attached, because the viewer swaps the canvas in only when a render completes:

```ts
import { expect, test } from '@playwright/test';

test('shows the report and navigates', async ({ page }) => {
  await page.goto('/reports/42');
  await expect(page.locator('.rpv-page canvas').first()).toBeAttached();

  const toolbar = page.getByRole('toolbar', { name: 'PDF controls' });
  await toolbar.getByRole('button', { name: 'Next page', exact: true }).click();
  await expect(toolbar).toContainText('2 /');
});
```

Query controls by role and accessible name, as above; the names come from `labels`, so tests stay stable across styling changes. If you translate labels, use the translated names.

## Accessibility checks

Run axe in your browser tests on screens that include the viewer. The viewer itself is tested against WCAG 2.2 AA in all its states; checking your pages catches issues in your surrounding UI and theming (for example color contrast).

```ts
import AxeBuilder from '@axe-core/playwright';

const results = await new AxeBuilder({ page })
  .withTags(['wcag2a', 'wcag2aa', 'wcag22aa'])
  .analyze();
expect(results.violations).toEqual([]);
```

## Deterministic states

To test your loading and error UI, pass a `fetcher` that never resolves or returns an error response, as the [Loading states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/loading-states/) and [Authenticated requests](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/authenticated-requests/) demos do.

## Related

- [Accessibility](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/accessibility/)
- [Events](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/events/)
- [Error states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/error-states/)

---

# Localization

> Ship the viewer in several languages with translated labels, locale-aware numbers and translated error messages.

Guides · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/localization/

## Keep one labels object per language

Store viewer labels next to the rest of your app's translations and pick the object for the active language. Define each object once (module scope or memoised), because labels are merged per object identity.

```tsx
import type { PdfViewerLabels } from '@kingtech/react-pdf-viewer';

const VIEWER_LABELS: Record<'en' | 'de', Partial<PdfViewerLabels>> = {
  en: {},
  de: {
    zoomIn: 'Vergrößern',
    zoomOut: 'Verkleinern',
    previousPage: 'Vorherige Seite',
    nextPage: 'Nächste Seite',
    moreActions: 'Weitere Aktionen',
    pageAriaLabel: (page, total, { formatNumber }) =>
      `Seite ${formatNumber(page)} von ${formatNumber(total)}`,
  },
};

<PdfViewer source={url} locale={language} labels={VIEWER_LABELS[language]} />;
```

The [`PdfViewerLabels` reference](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/api/pdf-viewer-labels/) lists every key with its English default; translate the accessible names too, not only visible text.

## Format numbers with the locale

`locale` controls how numbers in labels are formatted, including non-Latin digits. Function labels receive `formatNumber` for exactly this reason. See the live demo on [Labels and locale](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/labels/#customization).

## Translate error messages

`PdfViewerError.message` is English. Translate by `code` in `renderError`:

```tsx
const ERRORS: Record<PdfViewerErrorCode, string> = {
  HTTP_ERROR: 'Das Dokument konnte nicht geladen werden.',
  NETWORK_ERROR: 'Keine Verbindung zum Server.',
  // …one entry per code
};

<PdfViewer
  source={url}
  renderError={(error, { retry }) => <ErrorPanel message={ERRORS[error.code]} onRetry={retry} />}
/>;
```

For HTTP errors your API describes, `getHttpErrorMessage` can return the server's localised message; see [Authenticated requests](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/authenticated-requests/#customization).

## Right-to-left pages

The viewer works inside right-to-left pages, but its toolbar keeps a left-to-right order. For a mirrored toolbar, render your own with [`renderToolbar`](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/custom-toolbar/). PDF content is rendered as authored.

## Related

- [Labels and locale](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/labels/)
- [Error states](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/error-states/)
- [Accessibility](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/accessibility/)

---

# Accessibility

> What React PDF Viewer does for keyboard and screen reader users, how it is tested, and what remains your responsibility.

Guides · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/accessibility/

## What the viewer provides

- **Structure.** The viewer is a named region; the document area is a named, keyboard-scrollable group; each page is exposed as an image named “Page X of N”, or as a group containing its text and links when the [text layer](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/text-layer/) or [links](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/links/) are on.
- **Toolbar.** A WAI-ARIA toolbar with a single tab stop and arrow-key navigation. Every control has an accessible name; disabled controls use the `disabled` attribute. The overflow **More actions** menu follows the menu button pattern.
- **Announcements.** The page indicator is a polite live region; loading uses `role="status"`; errors and wrong passwords use `role="alert"`; search results are announced as they change.
- **Keyboard.** Every action is reachable from the keyboard, with [shortcuts](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/keyboard-shortcuts/) for navigation, zoom, rotation and fullscreen that never fire while typing.
- **Focus.** Focus is always visible. The overlay fullscreen mode traps focus, closes with <kbd>Esc</kbd> and returns focus to where it was.
- **Reduced motion.** Animations respect `prefers-reduced-motion`.

## How it is tested

Axe checks WCAG 2.2 A and AA rules in the automated browser tests: the viewer in its inline, fullscreen, loading, error and empty states on desktop and mobile sizes, and every page of this documentation site in light and dark color schemes. A keyboard walkthrough test checks the tab order and the toolbar's single tab stop.

Automated checks catch many issues but not all; if you find a barrier, please [report it](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/support/).

## What remains yours

- **Name multiple viewers.** Give each viewer on a page a distinct `aria-label`.
- **Keep contrast when theming.** Changing `--rpv-toolbar-bg`, `--rpv-toolbar-fg` or `--rpv-accent` can break the 4.5:1 text contrast and 3:1 focus indicator contrast. Check your theme with axe.
- **Custom views.** `renderToolbar`, `renderError`, `renderLoading` and `renderPasswordPrompt` replace built-in accessible markup; see [Overriding structure](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/overriding-structure/#keep-what-the-default-views-provide).
- **Headless layouts.** Label every `PdfPageCanvas` and provide your own keyboard support; see [Headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/#accessibility-in-headless-layouts).
- **The documents.** A scanned PDF without text cannot be read by screen readers, and the viewer cannot add structure a PDF lacks. Provide accessible alternatives for important documents.
- **Translations.** Translate accessible names along with visible text; see [Localization](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/localization/).

## Related

- [Keyboard shortcuts](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/keyboard-shortcuts/)
- [Testing](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/testing/#accessibility-checks)
- [Text layer](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/text-layer/)

---

# Security

> How React PDF Viewer handles untrusted documents, which defaults protect you, and how to deploy it under a strict Content Security Policy.

Guides · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/security/

## Defaults that protect you

- **A patched PDF.js.** The `pdfjs-dist` peer range (`^6.0.0`) excludes versions affected by CVE-2024-4367, which allowed arbitrary JavaScript through crafted fonts. The viewer also passes `isEvalSupported: false` as defence in depth.
- **No script injection or globals.** The viewer never adds `<script>` tags and never reads or writes globals such as `window.pdfjsLib`; PDF.js is imported as a module.
- **Safe external links.** With [links](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/links/) enabled, external links open in a new tab with `rel="noopener noreferrer nofollow"`, so the opened site cannot reach back into your page.
- **Local files stay local.** `File`, `Blob` and byte sources are read in the browser and never uploaded.
- **No JavaScript from documents.** PDF JavaScript actions are not executed.

## Treat documents as untrusted input

PDFs from users or third parties are parsed by PDF.js in a Web Worker. Keep `pdfjs-dist` up to date within the supported range so parser fixes reach your users, and follow [PDF.js security advisories](https://github.com/mozilla/pdf.js/security).

## Content Security Policy

A strict policy works when the PDF.js worker and assets are self-hosted:

```text
default-src 'self';
script-src 'self' 'wasm-unsafe-eval';
worker-src 'self' blob:;
connect-src 'self';
img-src 'self' blob: data:;
style-src 'self';
```

- `worker-src` must allow the worker's origin (and `blob:` if you create the worker from a blob).
- `connect-src` must allow the document and asset URLs.
- `'wasm-unsafe-eval'` is only needed for documents with JPEG 2000 or JBIG2 images, which use WebAssembly decoders.
- `'unsafe-eval'` is not needed.

See [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/#content-security-policy).

## Credentials and passwords

- `requestInit` and `fetcher` send whatever credentials you give them; scope them to the document endpoints.
- Document passwords passed through `password` or the prompt are held in memory by your app and the viewer; clear them from your state when they are no longer needed.
- Owner restrictions inside PDFs (for example “no printing”) are not enforced by the viewer; hide `print`, `download` and the text layer for such documents if they matter to you.

## Reporting vulnerabilities

Report vulnerabilities privately; see [Support](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/support/#reporting-a-security-issue) and [SECURITY.md](https://github.com/kiralygyula92/react-pdf-viewer/blob/main/SECURITY.md).

## Related

- [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/)
- [Authenticated requests](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/authenticated-requests/)
- [Requirements](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/requirements/)

---

# Vite

> Set up React PDF Viewer in a Vite app with a self-hosted PDF.js worker and assets.

Integrations · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/vite/

## Before you start

- Tested with Vite 8.3, `@vitejs/plugin-react` 6.1, React 19.3, `pdfjs-dist` 6.3 and TypeScript 6.0. The consumer smoke test type-checks and builds this setup against the packed package on every CI run.
- Install the package first: see [Installation](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/installation/).

## Setup

**1. Configure the worker in your entry module.** Vite's `?url` suffix emits the worker as a hashed asset and returns its URL:

```tsx
// src/main.tsx
import { configurePdfJs } from '@kingtech/react-pdf-viewer';
import '@kingtech/react-pdf-viewer/styles.css';
import workerSrc from 'pdfjs-dist/build/pdf.worker.min.mjs?url';

configurePdfJs({ workerSrc });
```

**2. Render the viewer** anywhere in your app:

```tsx
import { PdfViewer } from '@kingtech/react-pdf-viewer';

export function Report() {
  return <PdfViewer source="/report.pdf" fileName="report.pdf" />;
}
```

**3. Optional: self-host the other assets.** To serve CMaps, standard fonts, WebAssembly decoders and ICC profiles from your origin, copy them at build time, for example with `vite-plugin-static-copy`:

```ts
// vite.config.ts
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import { viteStaticCopy } from 'vite-plugin-static-copy';

export default defineConfig({
  plugins: [
    react(),
    viteStaticCopy({
      targets: ['cmaps', 'standard_fonts', 'wasm', 'iccs'].map((dir) => ({
        src: `node_modules/pdfjs-dist/${dir}`,
        dest: 'pdfjs',
      })),
    }),
  ],
});
```

Then point `configurePdfJs` at them:

```ts
configurePdfJs({
  workerSrc,
  cMapUrl: '/pdfjs/cmaps/',
  standardFontDataUrl: '/pdfjs/standard_fonts/',
  wasmUrl: '/pdfjs/wasm/',
  iccUrl: '/pdfjs/iccs/',
});
```

This documentation site uses the same copy step.

## Verify

1. Run `vite build` and check that `dist/assets/` contains a `pdf.worker.min-*.mjs` file and that the CSS bundle includes `.rpv-root`.
2. Run `vite preview`, open the page and confirm the first page renders.
3. In the browser's network panel, the worker loads from your origin, not from `cdn.jsdelivr.net`, and the development notice about CDN assets is gone.

## Known issues

- **Worker served with the wrong MIME type.** Some static hosts serve `.mjs` as `application/octet-stream`, and the browser refuses to start the worker. Configure the host to serve `.mjs` as `text/javascript`.
- **Assets under a base path.** With `base: '/app/'`, prefix the asset URLs too (`/app/pdfjs/cmaps/`), or build them from `import.meta.env.BASE_URL`.

## Related

- [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/)
- [Next.js](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/nextjs/)
- [webpack](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/webpack/)

---

# Next.js

> Use React PDF Viewer in the Next.js App Router, including Server Components and a self-hosted PDF.js worker.

Integrations · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/nextjs/

## Before you start

- Tested with Next.js 16.3 (App Router), React 19.3, `pdfjs-dist` 6.3 and TypeScript 6.0. The consumer smoke test builds this setup against the packed package on every CI run and checks that the viewer renders in the prerendered HTML.
- Install the package first: see [Installation](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/installation/).

## Setup

**1. Import the stylesheet in your root layout:**

```tsx
// app/layout.tsx
import type { ReactNode } from 'react';
import '@kingtech/react-pdf-viewer/styles.css';

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}
```

**2. Configure the worker in a client module.** The bundler emits the worker as an asset from `new URL(…, import.meta.url)`:

```tsx
// app/viewer.tsx
'use client';

import { configurePdfJs, PdfViewer } from '@kingtech/react-pdf-viewer';

configurePdfJs({
  workerSrc: new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString(),
});

export function Viewer({ source }: { source: string }) {
  return <PdfViewer source={source} />;
}
```

**3. Render it from a page.** Pages can stay Server Components:

```tsx
// app/page.tsx
import { Viewer } from './viewer';

export default function Page() {
  return <Viewer source="/report.pdf" />;
}
```

The package entry starts with `'use client'`, so a Server Component can also render `PdfViewer` directly with serializable props, such as a URL string. Callbacks, `ref` and `configurePdfJs` need a client module.

**4. Optional: self-host the other assets.** Copy the asset folders into `public/pdfjs/`, for example in a `postinstall` script:

```json
{
  "scripts": {
    "postinstall": "node -e \"for (const d of ['cmaps','standard_fonts','wasm','iccs']) require('fs').cpSync('node_modules/pdfjs-dist/'+d, 'public/pdfjs/'+d, {recursive: true})\""
  }
}
```

Then pass `cMapUrl: '/pdfjs/cmaps/'`, `standardFontDataUrl: '/pdfjs/standard_fonts/'`, `wasmUrl: '/pdfjs/wasm/'` and `iccUrl: '/pdfjs/iccs/'` to `configurePdfJs`, and add `public/pdfjs/` to `.gitignore`.

## Verify

1. Run `next build`. It must finish without “window is not defined” or similar server errors.
2. Open `.next/server/app/index.html`: it contains `class="rpv-root"` and the loading state, showing the viewer rendered on the server.
3. Run `next start`, open the page and confirm the document renders and the worker loads from your origin.

## Known issues

- **Callbacks from a Server Component.** Passing `onError` or other functions from a Server Component fails, because functions are not serializable. Render the viewer from a client module, as in step 2.
- **Document requests during prerendering.** Nothing is fetched on the server; documents load in the browser after hydration, so the prerendered HTML shows the loading state.

## Related

- [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/)
- [Vite](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/vite/)
- [Requirements](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/requirements/)

---

# webpack

> Set up React PDF Viewer with webpack 5, emitting the PDF.js worker as an asset and extracting the stylesheet.

Integrations · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/webpack/

## Before you start

- Tested with webpack 5.111, `css-loader` 7.1, `mini-css-extract-plugin` 2.9, React 19.3 and `pdfjs-dist` 6.3. The consumer smoke test builds this setup against the packed package on every CI run.
- Install the package first: see [Installation](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/installation/).

## Setup

**1. Configure the loaders.** The package ships ES modules and a plain CSS file:

```js
// webpack.config.mjs
import MiniCssExtractPlugin from 'mini-css-extract-plugin';

export default {
  entry: './src/main.jsx',
  output: {
    filename: '[name].[contenthash].js',
    assetModuleFilename: '[name].[contenthash][ext]',
    clean: true,
  },
  resolve: { extensions: ['.jsx', '.js', '.mjs'] },
  module: {
    rules: [
      // your JSX / TypeScript loader here
      { test: /\.m?js$/, resolve: { fullySpecified: false } },
      { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] },
    ],
  },
  plugins: [new MiniCssExtractPlugin()],
};
```

**2. Configure the worker in your entry module.** webpack 5 emits the file referenced by `new URL(…, import.meta.url)` as an asset:

```jsx
// src/main.jsx
import { configurePdfJs, PdfViewer } from '@kingtech/react-pdf-viewer';
import '@kingtech/react-pdf-viewer/styles.css';
import { createRoot } from 'react-dom/client';

configurePdfJs({
  workerSrc: new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString(),
});

createRoot(document.getElementById('root')).render(<PdfViewer source="/report.pdf" />);
```

**3. Optional: self-host the other assets** with `copy-webpack-plugin`:

```js
import CopyPlugin from 'copy-webpack-plugin';

new CopyPlugin({
  patterns: ['cmaps', 'standard_fonts', 'wasm', 'iccs'].map((dir) => ({
    from: `node_modules/pdfjs-dist/${dir}`,
    to: `pdfjs/${dir}`,
  })),
});
```

Then pass `cMapUrl: '/pdfjs/cmaps/'`, `standardFontDataUrl: '/pdfjs/standard_fonts/'`, `wasmUrl: '/pdfjs/wasm/'` and `iccUrl: '/pdfjs/iccs/'` to `configurePdfJs`.

## Verify

1. Run `webpack --mode production` and check that `dist/` contains a `pdf.worker.min.*.mjs` file and a CSS file that includes `.rpv-root`.
2. Serve `dist/`, open the page and confirm the first page renders and the worker loads from your origin.

## Known issues

- **“Can't resolve” errors for extensionless imports inside `pdfjs-dist`.** Strict ESM resolution rejects them; the `fullySpecified: false` rule in step 1 fixes it.
- **Worker served with the wrong MIME type.** Serve `.mjs` files as `text/javascript`, or the browser refuses to start the worker.

## Related

- [PDF.js configuration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/pdfjs-configuration/)
- [Vite](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/vite/)
- [Next.js](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/integrations/nextjs/)

---

# Migration

> Upgrade guides between versions of React PDF Viewer, one page per version jump with breaking changes.

Migration · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/migration/

Each release with breaking changes gets a guide here listing what changed, why, and the exact code to update. React PDF Viewer is at its initial release, so there are no upgrade guides yet. Check the [changelog](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/discover-more/changelog/) for every release and the [versioning policy](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/versions/#versioning-policy) for when breaking changes can happen.

---

# Roadmap

> How future work on React PDF Viewer is planned and how to propose or follow a feature.

Discover more · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/discover-more/roadmap/

React PDF Viewer does not publish dated commitments. Planned and requested work lives in public GitHub issues, so what is being considered is always visible and up to date.

## Follow and propose work

- **See what is requested:** browse [open feature requests](https://github.com/kiralygyula92/react-pdf-viewer/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement).
- **Propose a feature:** [open an issue](https://github.com/kiralygyula92/react-pdf-viewer/issues/new/choose) describing the problem you are solving, not only the API you would like. Upvote existing issues with a 👍 reaction instead of adding “+1” comments.
- **Know what shipped:** every release is listed in the [changelog](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/discover-more/changelog/).

## What shapes decisions

Proposals are weighed against the project's principles: new behavior is opt-in and never changes existing defaults, the package stays free of runtime dependencies, and every feature must be accessible and covered by automated tests. Each capability page's **Limitations** section lists what is known to be missing today, with a workaround.

## Related

- [Changelog](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/discover-more/changelog/)
- [Support](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/support/)
- [Features](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/all-features/)

---

# Changelog

> Every release of React PDF Viewer, newest first, with new features, fixes and breaking changes.

Discover more · https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/discover-more/changelog/

Releases follow the [versioning policy](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/versions/#versioning-policy) and are generated from changesets. Subscribe to the [RSS feed](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/discover-more/changelog/rss.xml) to hear about new releases.

## 1.0.1 (2026-10-19)

### Patch Changes

- Publish from GitHub Actions with provenance. The library's code and API are unchanged.

## 1.0.0 (2026-10-19)

First stable release of `PdfViewer`, an accessible, themeable React PDF viewer built on `pdfjs-dist`. From this release on, the public API (components, hooks, props, `PdfViewerApi`, labels, CSS classes and CSS variables) follows semantic versioning.

- Loads URLs, `URL` objects, `ArrayBuffer`, `Uint8Array`, `Blob` and `File` sources, with custom `fetch` options, a replaceable fetcher, HTTP error hooks and typed `PdfViewerError`s with retry. See [Document sources](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/document-sources/).
- Controlled or uncontrolled page, scale, rotation and fullscreen (`x` / `defaultX` / `onXChange`), plus an imperative `PdfViewerApi` through `ref`. See [Controlled state](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/controlled-state/).
- Cancellable, HiDPI, pixel-capped rendering that respects each page's intrinsic rotation. Memory stays flat in long documents: pages and thumbnails far from view release their canvases and cached page resources. See [Rendering](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/rendering/).
- A single-row responsive toolbar that collapses actions into a “More actions” menu, keyboard shortcuts, WAI-ARIA toolbar and menu semantics, and native, overlay or controlled fullscreen.
- Download, and printing through a hidden frame with every page on a sheet of its own size, or `printMode="open-url"` to hand the document to the browser's own viewer. See [Print](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/print/).
- Opt-in features: continuous layout, thumbnails, text layer, links, search, page-number input, zoom presets and reset, fit modes, Ctrl/⌘ + wheel zoom and a password prompt. See [Features](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/all-features/).
- Label overrides with locale-aware number formatting, 50+ CSS variables and dark / auto presets. See [Customization](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/customization/).
- Headless building blocks: `usePdfDocument`, `PdfPageCanvas`, `PdfToolbar` and `useControllableState`. See [Headless](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/headless/).
- Secure by default: `isEvalSupported: false`, a `pdfjs-dist` peer range that excludes CVE-2024-4367, external links opened with `rel="noopener noreferrer"`, no script injection, and SSR-safe rendering. See [Security](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/guides/security/).

## Related

- [Versions](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/getting-started/versions/)
- [Migration](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/migration/)
- [Roadmap](https://react-pdf-viewer-chi.vercel.app/react-pdf-viewer/discover-more/roadmap/)
