react-web-pdf-editor 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +442 -3
- package/dist/chunk-PAHIBB7W.js +246 -0
- package/dist/chunk-PAHIBB7W.js.map +1 -0
- package/dist/exportPdf-USRSK72H.js +341 -0
- package/dist/exportPdf-USRSK72H.js.map +1 -0
- package/dist/exportPdf.worker.js +20147 -0
- package/dist/exportPdf.worker.js.map +1 -0
- package/dist/index.cjs +9594 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +195 -0
- package/dist/index.d.ts +195 -0
- package/dist/index.js +8951 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +2934 -0
- package/docs/editor.png +0 -0
- package/package.json +59 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 react-pdf-editor contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,442 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# react-web-pdf-editor
|
|
2
|
+
|
|
3
|
+
View and edit PDFs in the browser with React. Open a file, annotate it, change the page order, and export a new PDF. Rendering uses [PDF.js](https://mozilla.github.io/pdf.js/). Export uses [pdf-lib](https://pdf-lib.js.org/).
|
|
4
|
+
|
|
5
|
+
The editor includes:
|
|
6
|
+
|
|
7
|
+
- Continuous page viewing, zoom, and in-document search
|
|
8
|
+
- Text boxes, freehand ink, highlights, images, and signatures
|
|
9
|
+
- A properties panel for the selected object
|
|
10
|
+
- Page thumbnails, blank pages, delete, and drag-to-reorder
|
|
11
|
+
- Undo and redo
|
|
12
|
+
- Export that flattens your edits into a downloadable PDF
|
|
13
|
+
|
|
14
|
+

|
|
15
|
+
|
|
16
|
+
Annotations stay in memory until you export. Changing the `file` prop starts a fresh document and clears them.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install react-web-pdf-editor
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Peer dependencies: `react` and `react-dom` 18 or later.
|
|
25
|
+
|
|
26
|
+
Import the stylesheet once. Without it, the toolbar, pages, and page canvas have no layout.
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { PdfEditor } from "react-web-pdf-editor";
|
|
30
|
+
import "react-web-pdf-editor/styles.css";
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { PdfEditor } from "react-web-pdf-editor";
|
|
37
|
+
import "react-web-pdf-editor/styles.css";
|
|
38
|
+
|
|
39
|
+
export function App() {
|
|
40
|
+
return <PdfEditor file="/contract.pdf" name="Service agreement" />;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`file` accepts a URL string, `URL`, `File`, `Blob`, `ArrayBuffer`, or `Uint8Array`. Leave it empty to show the empty state.
|
|
45
|
+
|
|
46
|
+
### Open a file from the user's computer
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { useState } from "react";
|
|
50
|
+
import { PdfEditor, type PdfSource } from "react-web-pdf-editor";
|
|
51
|
+
import "react-web-pdf-editor/styles.css";
|
|
52
|
+
|
|
53
|
+
export function App() {
|
|
54
|
+
const [file, setFile] = useState<PdfSource | null>(null);
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div style={{ height: "100vh" }}>
|
|
58
|
+
<input
|
|
59
|
+
type="file"
|
|
60
|
+
accept="application/pdf"
|
|
61
|
+
onChange={(event) => {
|
|
62
|
+
const next = event.target.files?.[0];
|
|
63
|
+
if (next) {
|
|
64
|
+
setFile(next);
|
|
65
|
+
}
|
|
66
|
+
}}
|
|
67
|
+
/>
|
|
68
|
+
<PdfEditor file={file} style={{ height: "calc(100vh - 40px)" }} />
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Give the editor a height. The root fills its parent (`height: 100%`), so a parent with no height collapses the canvas.
|
|
75
|
+
|
|
76
|
+
Remote URLs are fetched in the browser, with a download progress state when the server sends `Content-Length`. The PDF host must allow your origin through CORS. A `File` from an `<input>` does not have that restriction.
|
|
77
|
+
|
|
78
|
+
## Using the editor
|
|
79
|
+
|
|
80
|
+
The built-in chrome has a toolbar, a page-thumbnail sidebar, the document, and a properties panel. The toolbar holds the document name, tools, search, zoom, the page count, and **Export PDF**. The sidebar lists every page and ends with **Add blank page**. The properties panel stays open on the right; until something is selected it reads “Select a tool or annotation to edit properties.”
|
|
81
|
+
|
|
82
|
+
On widths under 900px the same editor switches to a compact layout: a slim top bar, a bottom tool dock, pages in a left drawer, and properties in a sheet.
|
|
83
|
+
|
|
84
|
+
### Tools
|
|
85
|
+
|
|
86
|
+
| Tool | What it does |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| Select | Move, resize, and rotate text, ink, highlights, images, and signatures. Click empty canvas to clear the selection. |
|
|
89
|
+
| Text | Click the page to place a box, then type. Double-click an existing box to edit it. |
|
|
90
|
+
| Draw | Draw freehand ink. Pick a color and a width (thin, pen, marker, bold) in the properties panel. |
|
|
91
|
+
| Highlight | Drag across existing PDF text to mark those lines, or drag on a blank area to paint a rectangle. |
|
|
92
|
+
| Signature | Opens a dialog to draw, type, or upload a signature, then click the page to place it. |
|
|
93
|
+
| Image | Opens a dialog to choose a PNG, JPG, or WebP, then click the page to place it. |
|
|
94
|
+
|
|
95
|
+
Select an object to edit it in the properties panel:
|
|
96
|
+
|
|
97
|
+
- **Text:** content, font, size, color, bold, italic, alignment, rotation
|
|
98
|
+
- **Ink:** color and stroke width
|
|
99
|
+
- **Highlight:** color
|
|
100
|
+
- **Image:** opacity, rotation, and replace
|
|
101
|
+
- **Signature:** rotation
|
|
102
|
+
|
|
103
|
+
Text fonts in the panel are Arial, Helvetica, Times New Roman, Georgia, Courier New, and Verdana. On export, text is painted as it appears on screen so the saved PDF matches the editor.
|
|
104
|
+
|
|
105
|
+
Images are limited to 8 MB and are scaled so the longest edge is at most 2400 pixels. Signatures can be drawn, typed, or uploaded (PNG, JPG, or WebP, up to 4 MB). Typed signatures use script fonts loaded from Google Fonts, so a strict Content-Security-Policy that blocks `fonts.googleapis.com` will fall back to a plainer face. The dialog keeps up to five recent signatures in `localStorage` on this browser.
|
|
106
|
+
|
|
107
|
+
Press Delete or Backspace to remove the selected object. Press Escape to leave the current tool, close a compact drawer, or clear the selection.
|
|
108
|
+
|
|
109
|
+
### Pages
|
|
110
|
+
|
|
111
|
+
The sidebar shows a thumbnail for every page, including your annotations.
|
|
112
|
+
|
|
113
|
+
- Click a thumbnail to jump to that page.
|
|
114
|
+
- Use **Add blank page** to insert a blank page after the current page.
|
|
115
|
+
- Delete a page from its thumbnail. The last remaining page cannot be deleted.
|
|
116
|
+
- Drag a thumbnail to reorder. With a thumbnail focused, Alt+Arrow also moves it.
|
|
117
|
+
|
|
118
|
+
Page insert, delete, and reorder rewrite the working PDF. They are disabled while a page change is still saving.
|
|
119
|
+
|
|
120
|
+
### Find, zoom, and rename
|
|
121
|
+
|
|
122
|
+
Search is in the toolbar. Ctrl+F or Cmd+F focuses it. Enter jumps to the next match, Shift+Enter to the previous one, and Escape clears it. Matches scroll into view.
|
|
123
|
+
|
|
124
|
+
Zoom from the toolbar, with Ctrl or Cmd plus `+` / `-`, or with Ctrl or Cmd and the mouse wheel while the pointer is over the editor. The default range is 50% to 300%, starting at 100%.
|
|
125
|
+
|
|
126
|
+
The document name in the toolbar is editable. Click it, type a new name, and press Enter. That name is the default export filename. Characters that are illegal in file names (`/ \ ? % * : | " < >`) are stripped.
|
|
127
|
+
|
|
128
|
+
### Export
|
|
129
|
+
|
|
130
|
+
**Export PDF** in the toolbar builds a PDF and saves it. The file includes the current page order and every annotation flattened onto the pages. If nothing has been annotated, export returns the current PDF bytes, including any page changes.
|
|
131
|
+
|
|
132
|
+
The download name is the document name, with `.pdf` added when it is missing. Set `exportFileName` when the download name should differ from the name shown in the toolbar.
|
|
133
|
+
|
|
134
|
+
Export runs in a worker and times out after 60 seconds. If the worker cannot start, export continues on the main thread.
|
|
135
|
+
|
|
136
|
+
### Keyboard shortcuts
|
|
137
|
+
|
|
138
|
+
These apply while focus is inside the editor and you are not typing in a text field or dialog.
|
|
139
|
+
|
|
140
|
+
| Shortcut | Action |
|
|
141
|
+
| --- | --- |
|
|
142
|
+
| Ctrl+Z / Cmd+Z | Undo |
|
|
143
|
+
| Ctrl+Y or Ctrl+Shift+Z / Cmd+Shift+Z | Redo |
|
|
144
|
+
| Ctrl+F / Cmd+F | Find |
|
|
145
|
+
| Ctrl or Cmd + `+` / `-` | Zoom in / out |
|
|
146
|
+
| Ctrl or Cmd + mouse wheel | Zoom |
|
|
147
|
+
| Delete / Backspace | Delete the selected object |
|
|
148
|
+
| Escape | Close a drawer, stop editing, or leave the current tool |
|
|
149
|
+
| Left / Right | Previous / next page |
|
|
150
|
+
| Up / Down | Scroll the page |
|
|
151
|
+
| Page Up / Page Down | Scroll about one screen |
|
|
152
|
+
| Home / End | Jump to the top or bottom of the document |
|
|
153
|
+
|
|
154
|
+
Undo covers annotation edits, page changes, and renames.
|
|
155
|
+
|
|
156
|
+
## Props
|
|
157
|
+
|
|
158
|
+
| Prop | Type | Default | Description |
|
|
159
|
+
| --- | --- | --- | --- |
|
|
160
|
+
| `file` | `PdfSource \| null` | — | PDF to open. Omit it, or pass `null`, to show the empty state. |
|
|
161
|
+
| `name` | `string` | File name, or `"document.pdf"` | Document name in the toolbar. When this prop changes, the editor follows it. |
|
|
162
|
+
| `onNameChange` | `(name: string) => void` | — | Called after a rename in the toolbar or an undo/redo that restores a name. |
|
|
163
|
+
| `className` | `string` | — | Extra class on the editor root. |
|
|
164
|
+
| `style` | `CSSProperties` | — | Inline style on the editor root. Use this to set height. |
|
|
165
|
+
| `page` | `number` | — | Controlled page number, starting at 1. |
|
|
166
|
+
| `defaultPage` | `number` | `1` | Initial page when `page` is not set. |
|
|
167
|
+
| `onPageChange` | `(page: number) => void` | — | Called when the visible page changes, including from scrolling. |
|
|
168
|
+
| `scale` | `number` | — | Controlled zoom. `1` is 100%. |
|
|
169
|
+
| `defaultScale` | `number` | `1` | Initial zoom when `scale` is not set. |
|
|
170
|
+
| `minScale` | `number` | `0.5` | Smallest zoom. |
|
|
171
|
+
| `maxScale` | `number` | `3` | Largest zoom. |
|
|
172
|
+
| `onScaleChange` | `(scale: number) => void` | — | Called when the zoom changes. |
|
|
173
|
+
| `showToolbar` | `boolean` | `true` | `false` hides the toolbar even if `toolbar` is set. |
|
|
174
|
+
| `toolbar` | `(api: ToolbarApi) => ReactNode` | Built-in toolbar | Render your own toolbar. Omit it to keep the default. |
|
|
175
|
+
| `showSidebar` | `boolean` | `true` | `false` hides the pages sidebar even if `sidebar` is set. |
|
|
176
|
+
| `sidebar` | `(api: SidebarApi) => ReactNode` | Built-in sidebar | Render your own pages sidebar. Omit it to keep the default. |
|
|
177
|
+
| `emptyState` | `ReactNode` | `"Open a PDF to get started."` | Shown when `file` is empty. |
|
|
178
|
+
| `workerSrc` | `string` | PDF.js worker on unpkg | URL of the PDF.js worker used to render pages. |
|
|
179
|
+
| `exportWorkerSrc` | `string` | Bundled export worker | URL of a custom export worker. |
|
|
180
|
+
| `exportFileName` | `string` | Document name | Download filename. `.pdf` is appended when missing. |
|
|
181
|
+
| `onExport` | `(blob: Blob) => void \| Promise<void>` | Browser download | Receive the exported PDF yourself. When this is set, the editor does not download the file. |
|
|
182
|
+
| `onExportError` | `(error: Error) => void` | — | Called when export fails. The toolbar also exposes `exportError`. |
|
|
183
|
+
| `onLoadSuccess` | `({ numPages }) => void` | — | Called after the PDF has loaded. |
|
|
184
|
+
| `onLoadError` | `(error: Error) => void` | — | Called when the PDF cannot be loaded. |
|
|
185
|
+
|
|
186
|
+
`PdfSource` is `string | URL | File | Blob | ArrayBuffer | Uint8Array`.
|
|
187
|
+
|
|
188
|
+
### Keep the page and zoom in your own state
|
|
189
|
+
|
|
190
|
+
Omit `page` and `scale` to let the editor own them. Pass them, and update them from the callbacks, to control them:
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
const [page, setPage] = useState(1);
|
|
194
|
+
const [scale, setScale] = useState(1);
|
|
195
|
+
|
|
196
|
+
<PdfEditor
|
|
197
|
+
file={file}
|
|
198
|
+
page={page}
|
|
199
|
+
scale={scale}
|
|
200
|
+
onPageChange={setPage}
|
|
201
|
+
onScaleChange={setScale}
|
|
202
|
+
/>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Do the same for the document name if a parent must stay in sync:
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
const [name, setName] = useState("Contract");
|
|
209
|
+
|
|
210
|
+
<PdfEditor file={file} name={name} onNameChange={setName} />
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Save the export yourself
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
<PdfEditor
|
|
217
|
+
file={file}
|
|
218
|
+
exportFileName="signed-contract.pdf"
|
|
219
|
+
onExport={async (blob) => {
|
|
220
|
+
const body = new FormData();
|
|
221
|
+
body.append("file", blob, "signed-contract.pdf");
|
|
222
|
+
await fetch("/api/documents", { method: "POST", body });
|
|
223
|
+
}}
|
|
224
|
+
onExportError={(error) => {
|
|
225
|
+
console.error(error);
|
|
226
|
+
}}
|
|
227
|
+
/>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`onExport` replaces the automatic download. Trigger a download yourself if you still want one:
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
onExport={(blob) => {
|
|
234
|
+
const url = URL.createObjectURL(blob);
|
|
235
|
+
const anchor = document.createElement("a");
|
|
236
|
+
anchor.href = url;
|
|
237
|
+
anchor.download = "signed-contract.pdf";
|
|
238
|
+
anchor.click();
|
|
239
|
+
URL.revokeObjectURL(url);
|
|
240
|
+
}}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Custom toolbar
|
|
244
|
+
|
|
245
|
+
`toolbar` receives a `ToolbarApi`. `showToolbar={false}` removes the bar entirely.
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
import { PdfEditor, Toolbar } from "react-web-pdf-editor";
|
|
249
|
+
|
|
250
|
+
<PdfEditor
|
|
251
|
+
file={file}
|
|
252
|
+
toolbar={(api) => (
|
|
253
|
+
<header>
|
|
254
|
+
<span>{api.name}</span>
|
|
255
|
+
<button type="button" onClick={api.undo} disabled={!api.canUndo}>
|
|
256
|
+
Undo
|
|
257
|
+
</button>
|
|
258
|
+
<button type="button" onClick={api.redo} disabled={!api.canRedo}>
|
|
259
|
+
Redo
|
|
260
|
+
</button>
|
|
261
|
+
<button type="button" onClick={() => api.setTool("text")}>
|
|
262
|
+
Text
|
|
263
|
+
</button>
|
|
264
|
+
<button type="button" onClick={api.zoomOut} disabled={api.scale <= api.minScale}>
|
|
265
|
+
−
|
|
266
|
+
</button>
|
|
267
|
+
<span>{Math.round(api.scale * 100)}%</span>
|
|
268
|
+
<button type="button" onClick={api.zoomIn} disabled={api.scale >= api.maxScale}>
|
|
269
|
+
+
|
|
270
|
+
</button>
|
|
271
|
+
<span>
|
|
272
|
+
{api.page} / {api.numPages}
|
|
273
|
+
</span>
|
|
274
|
+
<button type="button" onClick={api.exportPdf} disabled={api.disabled || api.exporting}>
|
|
275
|
+
{api.exporting ? "Saving…" : "Save"}
|
|
276
|
+
</button>
|
|
277
|
+
</header>
|
|
278
|
+
)}
|
|
279
|
+
/>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Render the exported `Toolbar` when you want the default bar inside your own layout:
|
|
283
|
+
|
|
284
|
+
```tsx
|
|
285
|
+
<PdfEditor file={file} toolbar={(api) => <Toolbar {...api} />} />
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
`ToolbarApi` fields:
|
|
289
|
+
|
|
290
|
+
| Field | Description |
|
|
291
|
+
| --- | --- |
|
|
292
|
+
| `name`, `setName` | Document name. `setName` goes through the same rename path as the built-in field. |
|
|
293
|
+
| `disabled` | `true` while no PDF is loaded. |
|
|
294
|
+
| `canUndo`, `canRedo`, `undo`, `redo` | History. `undoLabel` and `redoLabel` describe the next step when present. |
|
|
295
|
+
| `shortcuts` | Platform undo/redo labels: `{ undo, redo, undoKeys, redoKeys }`. |
|
|
296
|
+
| `activeTool`, `setTool` | `"select"`, `"draw"`, `"text"`, `"highlight"`, `"image"`, or `"signature"`. |
|
|
297
|
+
| `scale`, `minScale`, `maxScale`, `setScale`, `zoomIn`, `zoomOut` | Zoom. |
|
|
298
|
+
| `page`, `numPages`, `setPage`, `prevPage`, `nextPage` | Page navigation. |
|
|
299
|
+
| `search` | `{ query, setQuery, matchCount, currentIndex, next, prev, clear }`. `currentIndex` is `-1` when there is no active match. |
|
|
300
|
+
| `exportPdf`, `exporting`, `exportError` | Start an export, and read its pending or failed state. |
|
|
301
|
+
|
|
302
|
+
## Custom sidebar
|
|
303
|
+
|
|
304
|
+
`sidebar` receives a `SidebarApi`. `showSidebar={false}` removes the sidebar entirely.
|
|
305
|
+
|
|
306
|
+
```tsx
|
|
307
|
+
import { PdfEditor, PagesSidebar } from "react-web-pdf-editor";
|
|
308
|
+
|
|
309
|
+
<PdfEditor
|
|
310
|
+
file={file}
|
|
311
|
+
sidebar={(api) => (
|
|
312
|
+
<aside>
|
|
313
|
+
{api.pages.map((page) => (
|
|
314
|
+
<button
|
|
315
|
+
key={page.pageNumber}
|
|
316
|
+
type="button"
|
|
317
|
+
aria-current={page.selected ? "page" : undefined}
|
|
318
|
+
onClick={() => api.setPage(page.pageNumber)}
|
|
319
|
+
>
|
|
320
|
+
Page {page.label}
|
|
321
|
+
</button>
|
|
322
|
+
))}
|
|
323
|
+
<button
|
|
324
|
+
type="button"
|
|
325
|
+
onClick={() => api.addBlankPage(api.page)}
|
|
326
|
+
disabled={!api.canAdd || api.mutating}
|
|
327
|
+
>
|
|
328
|
+
Add page
|
|
329
|
+
</button>
|
|
330
|
+
</aside>
|
|
331
|
+
)}
|
|
332
|
+
/>
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Keep the default thumbnails by rendering `PagesSidebar`:
|
|
336
|
+
|
|
337
|
+
```tsx
|
|
338
|
+
<PdfEditor file={file} sidebar={(api) => <PagesSidebar {...api} />} />
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
`SidebarApi` fields:
|
|
342
|
+
|
|
343
|
+
| Field | Description |
|
|
344
|
+
| --- | --- |
|
|
345
|
+
| `pdf` | The PDF.js document, or `null` while loading. |
|
|
346
|
+
| `page`, `numPages`, `setPage` | Visible page and navigation. |
|
|
347
|
+
| `pages` | Visual order. Each item has `pageNumber`, `index`, `label`, `selected`, and `justAdded`. |
|
|
348
|
+
| `addBlankPage(afterPage)` | Insert a blank page after a 1-based page number. |
|
|
349
|
+
| `deletePage(pageNumber)` | Remove a page. No effect when only one page remains. |
|
|
350
|
+
| `reorderPages(from, to)` | Move a page. `from` and `to` are 1-based positions in the visual order. |
|
|
351
|
+
| `disabled` | `true` while no PDF is loaded. |
|
|
352
|
+
| `mutating` | `true` while a page insert, delete, or reorder is saving. |
|
|
353
|
+
| `adding` | `true` while a blank page is being inserted. |
|
|
354
|
+
| `canAdd`, `canDelete`, `canReorder` | Whether that page action is currently allowed. |
|
|
355
|
+
| `error` | Message from the last failed page change, or `null`. |
|
|
356
|
+
| `drawStrokes`, `highlights`, `textAnnotations`, `signatures`, `images` | Current annotations, used by the default thumbnails. |
|
|
357
|
+
|
|
358
|
+
`pageNumber` is the page's id in the loaded PDF. `label` is its position in the sidebar, which changes after a reorder.
|
|
359
|
+
|
|
360
|
+
## Workers
|
|
361
|
+
|
|
362
|
+
PDF.js renders pages on a worker. The default worker URL is a version-matched file on unpkg:
|
|
363
|
+
|
|
364
|
+
```text
|
|
365
|
+
https://unpkg.com/pdfjs-dist@<version>/build/pdf.worker.min.mjs
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Self-host that file and pass `workerSrc` when the CDN is blocked or you want the worker on your own origin. Copy it from `node_modules/pdfjs-dist/build/pdf.worker.min.mjs`.
|
|
369
|
+
|
|
370
|
+
```tsx
|
|
371
|
+
<PdfEditor file={file} workerSrc="/pdf.worker.min.mjs" />
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
You can also set it once, before the editor mounts:
|
|
375
|
+
|
|
376
|
+
```tsx
|
|
377
|
+
import { configurePdfWorker } from "react-web-pdf-editor";
|
|
378
|
+
|
|
379
|
+
configurePdfWorker("/pdf.worker.min.mjs");
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Export uses a second worker shipped with this package. Vite, Webpack, and other bundlers that understand `new URL(..., import.meta.url)` resolve it automatically. Pass `exportWorkerSrc` only if you need to host that worker at a specific URL. A failed worker start falls back to exporting on the main thread.
|
|
383
|
+
|
|
384
|
+
## Next.js
|
|
385
|
+
|
|
386
|
+
Load the editor in the browser. The package talks to workers, canvas, and `window` during render.
|
|
387
|
+
|
|
388
|
+
```tsx
|
|
389
|
+
"use client";
|
|
390
|
+
|
|
391
|
+
import dynamic from "next/dynamic";
|
|
392
|
+
import "react-web-pdf-editor/styles.css";
|
|
393
|
+
|
|
394
|
+
const PdfEditor = dynamic(
|
|
395
|
+
() => import("react-web-pdf-editor").then((mod) => mod.PdfEditor),
|
|
396
|
+
{ ssr: false },
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
export function DocumentEditor() {
|
|
400
|
+
return <PdfEditor file="/contract.pdf" style={{ height: "100vh" }} />;
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## Styling
|
|
405
|
+
|
|
406
|
+
Import `react-web-pdf-editor/styles.css` once at the app root. Classes are prefixed with `rpe-`. Pass `className` and `style` to the root when you need to size or position the editor:
|
|
407
|
+
|
|
408
|
+
```tsx
|
|
409
|
+
<PdfEditor
|
|
410
|
+
file={file}
|
|
411
|
+
className="contract-editor"
|
|
412
|
+
style={{ height: "100vh", width: "100%" }}
|
|
413
|
+
/>
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
The editor fills the height you give it. Below 900px of editor width, that same layout uses the compact drawers described above. Desktop layout is unchanged above that width.
|
|
417
|
+
|
|
418
|
+
## Exported types
|
|
419
|
+
|
|
420
|
+
These types are exported for custom toolbars, sidebars, and annotation handling:
|
|
421
|
+
|
|
422
|
+
`PdfEditorProps`, `PdfSource`, `PdfLoadSuccess`, `Tool`, `ToolbarApi`, `ToolbarSearchState`, `ToolbarShortcuts`, `SidebarApi`, `SidebarPage`, `TextAnnotation`, `DrawStroke`, `DrawPoint`, `HighlightAnnotation`, `HighlightRect`, `ImageAnnotation`, `PendingImage`, `SignatureAnnotation`, `PendingSignature`.
|
|
423
|
+
|
|
424
|
+
## Local development
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
npm install
|
|
428
|
+
npm run dev
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
The playground runs at `http://localhost:3000` and loads the library from `src/`. If port 3000 is already in use, the server does not start.
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
npm run build
|
|
435
|
+
npm run typecheck
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
`npm run build` writes ESM, CommonJS, and TypeScript declarations to `dist/`, and copies `styles.css` there. `prepublishOnly` runs that build before `npm publish`.
|
|
439
|
+
|
|
440
|
+
## License
|
|
441
|
+
|
|
442
|
+
MIT
|