Skip to content

React

Scope: React viewer toolkit (@scaryterry/pdfium/react, @scaryterry/pdfium/react/headless, @scaryterry/pdfium/react/editor).

Use this page to get from zero to a working viewer or editor, then choose your customization depth.

The React surface is organised around one core rule: the viewer is the document frame, and the editor layers on top of it.

@scaryterry/pdfium/react provides the shipped viewer shell and the lower-level viewer building blocks:

  • PDFViewer for high-level integration.
  • useViewerSetup + slot components for custom layout control.
  • Low-level hooks/components for full headless composition.

@scaryterry/pdfium/react/headless now collects the complete headless contract, with optional focused subpaths for clearer ownership:

  • @scaryterry/pdfium/react/headless/session for provider/session access.
  • @scaryterry/pdfium/react/headless/viewer for viewer state, contexts, and composition primitives.
  • @scaryterry/pdfium/react/headless/viewer-layers for unstyled page/layer primitives.
  • @scaryterry/pdfium/react/headless/editor for editor provider/session and state hooks.
  • @scaryterry/pdfium/react/headless/editor-layers for editor interaction layers.
  • @scaryterry/pdfium/react/headless still re-exports all of the above when you prefer one import surface.

@scaryterry/pdfium/react/editor is additive:

  • PDFEditor is the canonical shipped editor shell.
  • Lower-level editor pieces remain public when you need to replace part of that shell.
  • Editor hooks/components sit beside the viewer instead of replacing it.
  • A valid PDF source (ArrayBuffer or URL/fetch flow in your app).
  • A worker entry module in your app bundle.
  • A WASM source (wasmUrl or wasmBinary).

PDFiumProvider requires:

  • workerUrl (URL to a module worker entry)
  • One of wasmUrl or wasmBinary
  • Optional initialDocument
Section titled “Recommended (bundler-managed worker module)”

Create a worker entry in your app:

src/pdfium.worker.ts
import '@scaryterry/pdfium/worker';

Then wire the provider:

import wasmUrl from '@scaryterry/pdfium/pdfium.wasm?url';
import { PDFiumProvider, PDFViewer } from '@scaryterry/pdfium/react';
const workerUrl = new URL('./pdfium.worker.ts', import.meta.url).toString();
function App() {
return (
<PDFiumProvider
wasmUrl={wasmUrl}
workerUrl={workerUrl}
initialDocument={{ data: pdfBytes, name: 'document.pdf' }}
>
<PDFViewer />
</PDFiumProvider>
);
}
  • Copy node_modules/@scaryterry/pdfium/dist/vendor/pdfium.wasm to public/pdfium.wasm
  • Keep the worker as a bundled module (src/pdfium.worker.ts) and pass its emitted URL as workerUrl

Do not copy only dist/worker.js by itself; it imports sibling modules.

Your baseline React setup is correct when:

  • PDFiumProvider mounts without init errors.
  • PDFViewer shows at least page 1 of your document.
  • No worker timeout or WASM load errors appear in the console.
  • PDFViewer — Compound viewer component and state surface.
  • Headless — Public session/viewer/editor composition without the shipped shell chrome.
  • @scaryterry/pdfium/react/headless — Use when you want product-owned viewer/editor chrome with the public React bindings.
  • @scaryterry/pdfium/react/headless/* — Use when you want more explicit session/viewer/editor module boundaries.
  • Editor — Annotation editing, save, and page-management composition.
  • ToolbarDefaultToolbar and headless PDFToolbar slot API.
  • useViewerSetup — Orchestration hook for navigation/zoom/layout.
  • Examples — End-to-end integration recipes.
  • Styling — Theming and visual customization.