npm package: @kopexa/pdf-viewer
Description: A PDF viewer component powered by react-pdf
Author: Kopexa
Latest version: 1.1.7

# @kopexa/pdf-viewer

A PDF viewer component powered by [react-pdf](https://github.com/wojtekmaj/react-pdf) with smooth loading transitions, i18n support, and text selection.

Part of the Kopexa Sight Design System.

- **Author:** Kopexa (<https://kopexa.com>)
- **License:** Apache-2.0
- **Repository:** [kopexa-grc/sight](https://github.com/kopexa-grc/sight)

## Installation

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

### Peer Dependencies

| Package     | Version      |
| ----------- | ------------ |
| `react`     | `>=19.0.0`   |
| `react-dom` | `>=19.0.0`   |
| `react-pdf` | `>=9.0.0`    |
| `motion`    | `>=12.23.6`  |

## Usage

### Worker Configuration

You **must** configure the PDF.js worker before rendering any `PDFViewer`. Call `configurePDFWorker()` once at application startup.

```ts
import { configurePDFWorker } from "@kopexa/pdf-viewer";

// Option 1: CDN (simplest, no bundler config needed)
configurePDFWorker();

// Option 2: Local worker via import.meta.url (recommended for production)
configurePDFWorker(
  new URL("pdfjs-dist/build/pdf.worker.min.mjs", import.meta.url).toString()
);

// Option 3: Custom URL (e.g. self-hosted worker)
configurePDFWorker("https://cdn.example.com/pdf.worker.min.mjs");
```

### Basic Example

```tsx
import { configurePDFWorker, PDFViewer } from "@kopexa/pdf-viewer";

configurePDFWorker();

function MyPage() {
  return (
    <PDFViewer
      source={{ type: "url", url: "/document.pdf" }}
      onDocumentLoad={(doc) => console.log(`Loaded ${doc.numPages} pages`)}
    />
  );
}
```

### With Next.js (App Router)

Since `pdfjs-dist` requires browser APIs, use `next/dynamic` with `ssr: false`:

```tsx
"use client";

import dynamic from "next/dynamic";

const PDFViewer = dynamic(
  () =>
    import("@kopexa/pdf-viewer").then((mod) => {
      mod.configurePDFWorker(
        new URL("pdfjs-dist/build/pdf.worker.min.mjs", import.meta.url).toString()
      );
      return { default: mod.PDFViewer };
    }),
  { ssr: false }
);

export default function Page() {
  return <PDFViewer source={{ type: "url", url: "/sample.pdf" }} />;
}
```

### Lazy-Loaded Variant

`PDFViewerLazy` wraps the viewer in `React.lazy` + `ClientOnly` so it only loads on the client with built-in loading state:

```tsx
import { configurePDFWorker, PDFViewerLazy } from "@kopexa/pdf-viewer";

configurePDFWorker();

function MyPage() {
  return <PDFViewerLazy source={{ type: "url", url: "/document.pdf" }} />;
}
```

### PDF Source Types

```tsx
// URL (react-pdf fetches internally)
<PDFViewer source={{ type: "url", url: "/report.pdf" }} />

// Binary data
<PDFViewer source={{ type: "data", data: myUint8Array }} />

// Base64-encoded string
<PDFViewer source={{ type: "base64", data: base64String }} />
```

## Features

- **Smooth transitions** — Animated fade from loading state to rendered pages via `motion`
- **Text selection** — Text and annotation layers are enabled by default
- **i18n** — All user-facing strings are translatable via `@kopexa/i18n` (EN + DE included)
- **Responsive** — Pages scale to container width automatically
- **Click tracking** — `onPageClick` provides page-relative coordinates
- **Custom renderers** — Pass `customPageRenderer` for custom page rendering
- **Error handling** — Built-in error state with customizable content

## Props

| Prop                 | Type                                       | Description                                    |
| -------------------- | ------------------------------------------ | ---------------------------------------------- |
| `source`             | `PDFSource`                                | The PDF source (url, data, or base64)          |
| `className`          | `string`                                   | CSS class for the root element                 |
| `onDocumentLoad`     | `(doc: LoadedPDFDocument) => void`         | Called when the document finishes loading       |
| `onPageClick`        | `OnPDFViewerPageClick`                     | Called with page-relative click coordinates     |
| `customPageRenderer` | `React.FunctionComponent`                  | Custom page renderer component                  |
| `options`            | `Record<string, unknown>`                  | PDF.js document options (e.g. `cMapUrl`)        |
| `loadingContent`     | `React.ReactNode`                          | Custom loading state content                    |
| `errorContent`       | `React.ReactNode`                          | Custom error state content                      |
| `pageLabel`          | `(page: number, total: number) => ReactNode` | Custom page label renderer                   |

## Contribution

Yes please! See the
[contributing guidelines](https://github.com/kopexa-grc/sight/blob/master/CONTRIBUTING.md)
for details.

## License

This project is licensed under the terms of the
[Apache-2.0 license](https://github.com/kopexa-grc/sight/blob/master/LICENSE).