A framework-agnostic, universal document renderer with optional chunked loading
polyrender.wisp.place/
1# @polyrender/react
2
3React component and hook for rendering documents in the browser. A thin wrapper around [`@polyrender/core`](https://www.npmjs.com/package/@polyrender/core) that handles React lifecycle, cleanup, and ref-based imperative control.
4
5Supports PDF, EPUB, DOCX, ODT, ODS, CSV/TSV, source code, plain text, and comic book archives (.cbz, .cbr, .cb7, .cbt).
6
7## Installation
8
9```bash
10npm install @polyrender/react @polyrender/core
11```
12
13Install peer dependencies only for the formats you need:
14
15```bash
16npm install pdfjs-dist # PDF
17npm install epubjs # EPUB
18npm install docx-preview # DOCX
19npm install jszip # ODT, CBZ comic archives
20npm install xlsx # ODS
21npm install papaparse # CSV/TSV
22npm install highlight.js # Code, Markdown, JSON, XML/HTML
23
24# Comic book archives — additional optional backends:
25npm install node-unrar-js # CBR (.cbr, RAR-compressed comics)
26npm install 7z-wasm # CB7 (.cb7, 7-Zip-compressed comics)
27
28# Comic book archives — optional exotic image format decoders:
29npm install @jsquash/jxl # JPEG XL images inside archives
30npm install utif # TIFF images inside archives
31```
32
33## Quick Start
34
35```tsx
36import { DocumentViewer } from '@polyrender/react'
37import '@polyrender/core/styles.css'
38
39function App() {
40 return (
41 <DocumentViewer
42 source={{ type: 'url', url: '/report.pdf' }}
43 theme="dark"
44 style={{ width: '100%', height: '80vh' }}
45 onReady={(info) => console.log(`${info.pageCount} pages`)}
46 onPageChange={(page, total) => console.log(`${page}/${total}`)}
47 />
48 )
49}
50```
51
52## `<DocumentViewer>`
53
54Drop-in component. Props mirror `PolyRenderOptions` from `@polyrender/core`.
55
56### Props
57
58| Prop | Type | Default | Description |
59|------|------|---------|-------------|
60| `source` | `DocumentSource \| null` | — | The document to render |
61| `format` | `DocumentFormat` | auto | Override format detection |
62| `theme` | `'dark' \| 'light' \| 'system'` | `'dark'` | Color theme |
63| `className` | `string` | — | Extra CSS class on the root element |
64| `style` | `React.CSSProperties` | — | Styles for the wrapper div (set width/height here) |
65| `initialPage` | `number` | `1` | Starting page |
66| `zoom` | `number \| 'fit-width' \| 'fit-page' \| 'auto'` | — | Initial zoom |
67| `toolbar` | `boolean \| ToolbarConfig` | `true` | Toolbar visibility/config. `ToolbarConfig` fields: `navigation`, `zoom`, `wrapToggle`, `fullscreen`, `info`, `download`, `position` |
68| `showPageNumbers` | `boolean` | — | Show page numbers |
69| `onReady` | `(info: DocumentInfo) => void` | — | Fired when document is loaded |
70| `onPageChange` | `(page, total) => void` | — | Fired on page navigation |
71| `onZoomChange` | `(zoom: number) => void` | — | Fired on zoom change |
72| `onError` | `(error: PolyRenderError) => void` | — | Fired on unrecoverable error |
73| `onLoadingChange` | `(loading: boolean) => void` | — | Fired on loading state change |
74| `pdf` | `PdfOptions` | — | PDF-specific options |
75| `epub` | `EpubOptions` | — | EPUB-specific options |
76| `code` | `CodeOptions` | — | Code-specific options |
77| `csv` | `CsvOptions` | — | CSV/TSV-specific options |
78| `comic` | `ComicOptions` | — | Comic archive options (JXL/TIFF decoders, format filter) |
79
80### Imperative Control via Ref
81
82```tsx
83import { useRef } from 'react'
84import { DocumentViewer, type DocumentViewerRef } from '@polyrender/react'
85import '@polyrender/core/styles.css'
86
87function App() {
88 const viewerRef = useRef<DocumentViewerRef>(null)
89
90 return (
91 <>
92 <DocumentViewer
93 ref={viewerRef}
94 source={{ type: 'url', url: '/report.pdf' }}
95 style={{ width: '100%', height: '80vh' }}
96 />
97 <button onClick={() => viewerRef.current?.goToPage(1)}>First page</button>
98 <button onClick={() => viewerRef.current?.setZoom('fit-width')}>Fit width</button>
99 </>
100 )
101}
102```
103
104`DocumentViewerRef` exposes: `goToPage(page)`, `setZoom(zoom)`, `getCurrentPage()`, `getPageCount()`, `getZoom()`.
105
106## `useDocumentRenderer` (headless hook)
107
108For building fully custom UI around the renderer:
109
110```tsx
111import { useDocumentRenderer } from '@polyrender/react'
112import '@polyrender/core/styles.css'
113
114function CustomViewer({ url }: { url: string }) {
115 const { containerRef, state, goToPage, setZoom } = useDocumentRenderer({
116 source: { type: 'url', url },
117 theme: 'dark',
118 toolbar: false,
119 })
120
121 return (
122 <div>
123 <div ref={containerRef} style={{ width: '100%', height: '600px' }} />
124 <div>
125 <button onClick={() => goToPage(state.currentPage - 1)}>Prev</button>
126 <span>{state.currentPage} / {state.totalPages}</span>
127 <button onClick={() => goToPage(state.currentPage + 1)}>Next</button>
128 <button onClick={() => setZoom(state.zoom * 1.2)}>Zoom In</button>
129 </div>
130 </div>
131 )
132}
133```
134
135### Return value
136
137| Field | Type | Description |
138|-------|------|-------------|
139| `containerRef` | `RefObject<HTMLDivElement>` | Attach to your container element |
140| `state` | `PolyRenderState` | Current viewer state |
141| `goToPage` | `(page: number) => void` | Navigate to a page |
142| `setZoom` | `(zoom) => void` | Set zoom level |
143| `ready` | `boolean` | `true` after `onReady` fires |
144| `error` | `PolyRenderError \| null` | Current error, if any |
145
146`PolyRenderState` contains: `loading`, `error`, `currentPage`, `totalPages`, `zoom`, `documentInfo`.
147
148## Document Sources
149
150See [`@polyrender/core`](https://www.npmjs.com/package/@polyrender/core) for full documentation on `FileSource`, `UrlSource`, `PagesSource`, and `ChunkedSource`.
151
152## Theming
153
154Import and apply styles from `@polyrender/core`:
155
156```tsx
157import '@polyrender/core/styles.css'
158```
159
160Override CSS custom properties on the `.polyrender` root element:
161
162```css
163.my-viewer .polyrender {
164 --dv-bg: #1e1e2e;
165 --dv-text: #cdd6f4;
166 --dv-accent: #89b4fa;
167}
168```
169
170## Repository
171
172The source code is hosted in two locations:
173
174- **Tangled** (primary): https://tangled.org/aria.pds.witchcraft.systems/polyrender
175- **GitHub** (mirror): https://github.com/BuyMyMojo/polyrender
176
177This package lives under `packages/react` in the monorepo.
178
179## License
180
181Zlib