VerifyKitv0.13.3

React Integration

@trexolab/verifykit-react provides a React PDF viewer with signature verification, a plugin architecture, and theme support. It uses @trexolab/verifykit-core for cryptographic verification, powered by a Rust/WASM engine.

Setup

Install

bash
npm install @trexolab/verifykit-react

pdf.js is bundled — you do not install it. Since v0.6.0 pdf.js 5.5.207 ships inside this package in a lazily-loaded chunk, so it is not in your package.json, not in your initial bundle, and npm ls pdfjs-dist will not list it. @trexolab/verifykit-core is installed automatically as a dependency.

Requirements: React 19+, modern browser with WebAssembly support. The workerUrl option is required in VerifyKitProvider config.

Import CSS

Add the stylesheet import at the top of your entry file:

tsx
import '@trexolab/verifykit-react/styles.css'

Without this import, the viewer renders as a blank white area.

Copy CMap and Font Files

For non-Latin text support (CJK, Arabic, etc.), copy the CMap and font files to your public directory:

bash
cp -r node_modules/@trexolab/verifykit-react/cmaps public/cmaps
cp -r node_modules/@trexolab/verifykit-react/standard_fonts public/standard_fonts

Add CSS Reset

css
html, body, #root {
  height: 100%;
  margin: 0;
  padding: 0;
}

Wrap with VerifyKitProvider

tsx
import { VerifyKitProvider } from '@trexolab/verifykit-react'
 
function App() {
  return (
    <VerifyKitProvider config={{
      workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
      theme: { mode: 'system' },
    }}>
      <div style={{ height: '100vh' }}>
        <MyViewer />
      </div>
    </VerifyKitProvider>
  )
}

VerifyKitProvider

The provider wraps your app to supply configuration, verifier instance, theme, and i18n context. It handles WASM initialization internally -- no setup code needed.

tsx
<VerifyKitProvider config={config}>
  <App />
</VerifyKitProvider>

Configuration

ts
interface VerifyKitConfig extends VerifyKitCoreConfig {
  workerUrl: string            // REQUIRED. URL to the PDF.js worker script.
  cMapUrl?: string              // Default: '/cmaps/'
  standardFontDataUrl?: string  // Default: '/standard_fonts/'
  theme?: {
    mode?: 'light' | 'dark' | 'system'
    overrides?: Record<string, string>  // CSS variable overrides
  }
  embeddedFont?: boolean | string
  toolbar?: ToolbarConfig
  locale?: string           // Default: 'en'
  translations?: Partial<TranslationStrings>
}

Key options:

  • workerUrl -- Required. URL to the PDF.js worker script. Use 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs' for CDN, or self-host the file and pass a local path like '/pdf.worker.min.mjs'.
  • theme.mode -- Set the initial theme to 'light', 'dark', or 'system' (follows OS preference).
  • cMapUrl / standardFontDataUrl -- Paths to CMap and font files for non-Latin text.
  • toolbar -- Configure which toolbar features are enabled.

useVerification Hook

The useVerification() hook manages PDF loading, verification, and result state. It creates an async verifier backed by the Rust/WASM core.

ts
const verification = useVerification()
 
// Load a file (File, ArrayBuffer, Uint8Array, or URL)
await verification.load(file)
 
// Access results
verification.fileBuffer      // ArrayBuffer | null — the loaded PDF bytes
verification.originalFileBuffer // ArrayBuffer | null — original file bytes (before appearance swap)
verification.fileName        // string — display name of the loaded file
verification.signatures      // PdfSignature[] — all verified signatures
verification.status          // 'valid' | 'invalid' | 'warning' | 'unknown' | 'pending' | null
verification.unsignedFields  // UnsignedSigField[] — unsigned signature fields
verification.isLoading       // boolean — true while verification is in progress
verification.error           // LoadError | null — error object if verification failed
verification.result          // VerificationResult | null — full verification result object
verification.metadata        // DocumentMetadata | null — PDF document metadata
verification.permissions     // DocumentPermissions | null — PDF security permissions
 
// Re-verify all signatures on the current buffer
await verification.revalidate()
 
// Reset state
verification.reset()

Next.js / SSR

The package is a client module (it ships 'use client') and is safe to import from a Server Component — pdf.js is loaded lazily inside effects, so it never runs on the server. No next/dynamic, no ssr: false, no wrapper file. See Deployment → Next.js.

useVerification() is also safe to call before the WASM verifier has finished initialising: load() waits for it rather than throwing, so calling it from a mount effect works.

Encrypted PDFs

Nothing to wire. When the viewer unlocks a password-protected PDF it hands the accepted password to useVerification through VerifyKitProvider, so the signature drawn on the page swaps from Adobe's yellow "?" to the validated icon just like an unencrypted document. The only requirement is that <Viewer> and useVerification() sit under the same provider — that provider is what carries the password between them.

applyPassword(pwd) remains available as an escape hatch for hosts that run their own PDF.js instance and need to report the password themselves:

ts
// Optional — only if your app unlocks the document outside VerifyKit's viewer.
await verification.applyPassword(userPassword)

Pair it with <Viewer onPasswordAccepted={verification.applyPassword} /> if you want the report to flow through props instead of the provider. Both paths are supported; calling both is harmless, as a repeated password is ignored.

The original signed bytes are never modified; the swap produces display-only bytes held in memory. Documents encrypted with AES-128 are supported. Other ciphers are left untouched and keep showing the "?" — see Troubleshooting.

Key concept: Verification vs Viewer

VerifyKit separates two concerns:

ConcernResponsibilityComponent
VerificationCryptographic signature validation via Rust/WASMuseVerification() hook
ViewerPDF rendering and UI features<Viewer> component with plugins

The <Viewer> does not perform verification -- it only displays pre-verified results. useVerification() loads the PDF, runs all cryptographic checks, and produces results that you pass to the viewer as props.

Viewer Component

The <Viewer> is the recommended component for displaying PDFs. It accepts plugins for composable features.

tsx
<Viewer
  ref={viewerRef}
  fileBuffer={verification.fileBuffer}
  fileName={verification.fileName}
  plugins={[layout.plugin]}
  onOpenFile={handleFile}
  signatures={verification.signatures}
  unsignedFields={verification.unsignedFields}
  verificationStatus={verification.status ?? undefined}
/>

Props

PropTypeDescription
fileBufferArrayBuffer | nullPDF file bytes
fileNamestringDisplay name
pluginsViewerPlugin[]Plugins to install
signaturesPdfSignature[]Reactive signature data (synced to store)
unsignedFieldsUnsignedSigField[]Reactive unsigned field data
verificationStatusVerificationStatusReactive overall verification status
verifyingbooleanShow a loading indicator while verification is in progress
initialStatePartial<ViewerStoreState>Initial store overrides (for advanced use)
onDocumentLoaded(doc) => voidCalled when PDF.js document is ready
onOpenFile(file) => voidFile open handler (enables drag-and-drop)
onLoadError(error: LoadError) => voidCalled when the PDF fails to load
onRevalidate() => voidCalled when user requests re-verification
signaturePanelOpenbooleanAuto-open the signature panel
isActivebooleanTab isolation — set to false to freeze state updates
renderError(error: LoadError) => ReactNodeCustom error UI renderer
renderLayout(slots) => ReactNodeCustom layout render function

Ref Handle

ts
const viewerRef = useRef<ViewerHandle>(null)
viewerRef.current.scrollToPage(5)

Plugins

Viewer features are provided by plugins. You compose only the plugins you need.

Batteries-Included (defaultLayoutPlugin)

The simplest approach -- all features with zero configuration:

tsx
import { defaultLayoutPlugin } from '@trexolab/verifykit-react'
 
function MyViewer() {
  const [layout] = useState(() => defaultLayoutPlugin())
 
  return <Viewer fileBuffer={buffer} plugins={[layout.plugin]} />
}

Always wrap plugin creation in useState to avoid infinite re-renders. Creating a plugin inside the render body without useState produces a new instance every render.

Selective Features

Disable specific features you do not need:

tsx
const [layout] = useState(() =>
  defaultLayoutPlugin({
    disable: { print: true, download: true, contextMenu: true },
  })
)

Configuring Features

Sub-plugins that take options are configured under their own key:

tsx
const [layout] = useState(() =>
  defaultLayoutPlugin({
    zoom: { minScale: 0.5, maxScale: 4 },
    // Floating previous/next control over the bottom of the page in
    // single-page scroll mode, on by default. Single-page is the one mode
    // where scrolling cannot reach the next page, so turning this off leaves
    // the toolbar as the only way forward.
    pageNavigation: { floatingNav: false },
  })
)

Individual Plugins

For maximum control, import and compose individual plugins:

tsx
import { zoomPlugin, pageNavigationPlugin, toolbarPlugin } from '@trexolab/verifykit-react'
 
const [zoom] = useState(() => zoomPlugin())
const [nav] = useState(() => pageNavigationPlugin())
const [toolbar] = useState(() => toolbarPlugin())
 
<Viewer fileBuffer={buffer} plugins={[zoom, nav, toolbar]} />

See the Plugins guide for a complete list of all 24 plugins with their APIs and options.

WelcomeScreen

A file open/drop landing screen shown before a PDF is loaded:

tsx
import { WelcomeScreen } from '@trexolab/verifykit-react'
 
if (!verification.fileBuffer) {
  return <WelcomeScreen onOpenFile={handleFile} />
}

The WelcomeScreen displays a drop zone where users can drag and drop PDF files or click to browse. Pass onOpenFile to receive the selected File object. The accept prop controls which file types are shown in the file picker (default: '.pdf,application/pdf').

Theme Configuration

Theme Modes

Set the theme via the provider config:

tsx
<VerifyKitProvider config={{
  workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
  theme: { mode: 'system' },
}}>

Available modes:

  • 'light' -- Light theme
  • 'dark' -- Dark theme
  • 'system' -- Follows the operating system preference

CSS Variable Overrides

Override CSS variables through the provider config:

tsx
<VerifyKitProvider config={{
  workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
  theme: {
    mode: 'dark',
    overrides: {
      '--primary': '#6366f1',
      '--primary-hover': '#4f46e5',
    },
  },
}}>

Runtime Theme Toggle

When using defaultLayoutPlugin, you can toggle the theme programmatically:

tsx
const [layout] = useState(() => defaultLayoutPlugin())
 
// Toggle between light and dark
layout.theme.toggleTheme()
 
// Set explicitly
layout.theme.setTheme('dark')

Common Patterns

Full Example: File Loading and Verification

tsx
import { useState, useCallback, useRef } from 'react'
import {
  VerifyKitProvider,
  Viewer,
  WelcomeScreen,
  useVerification,
  defaultLayoutPlugin,
} from '@trexolab/verifykit-react'
import '@trexolab/verifykit-react/styles.css'
 
function App() {
  return (
    <VerifyKitProvider config={{
      workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
      theme: { mode: 'system' },
    }}>
      <div style={{ height: '100vh' }}>
        <MyViewer />
      </div>
    </VerifyKitProvider>
  )
}
 
function MyViewer() {
  const verification = useVerification()
  const viewerRef = useRef(null)
  const [layout] = useState(() => defaultLayoutPlugin())
 
  const handleFile = useCallback(
    (file) => { verification.load(file) },
    [verification],
  )
 
  if (!verification.fileBuffer) {
    return <WelcomeScreen onOpenFile={handleFile} />
  }
 
  return (
    <Viewer
      ref={viewerRef}
      fileBuffer={verification.fileBuffer}
      fileName={verification.fileName}
      plugins={[layout.plugin]}
      onOpenFile={handleFile}
      signatures={verification.signatures}
      unsignedFields={verification.unsignedFields}
      verificationStatus={verification.status ?? undefined}
    />
  )
}

Subscribing to Viewer State

Use useViewerStore inside a component rendered within <Viewer> to subscribe to reactive state:

tsx
import { useViewerStore, useViewerStoreKey } from '@trexolab/verifykit-react'
 
// Select multiple values
const [page, total] = useViewerStore(s => [s.currentPage, s.totalPages])
 
// Select a single key
const scale = useViewerStoreKey('scale')

Next.js Setup

Since 0.6.0 there is no setup. The package ships a 'use client' directive, so it is its own client boundary, and pdf.js is loaded lazily inside effects so nothing browser-only is evaluated during a server render. Import it directly — including from a Server Component — with no next/dynamic, no ssr: false, and no wrapper file:

tsx
// app/page.tsx — a Server Component. Note: no 'use client' in this file.
import { VerifyKitProvider, Viewer } from '@trexolab/verifykit-react'
import '@trexolab/verifykit-react/styles.css'
 
export default function Page() {
  return (
    <VerifyKitProvider
      config={{ workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs' }}
    >
      <div style={{ height: '100vh' }}>
        <Viewer />
      </div>
    </VerifyKitProvider>
  )
}

Add 'use client' to your own file only when that file uses hooks itself — which is the case as soon as you call useVerification() to load a document and read results:

tsx
// app/verify/page.tsx
'use client'
 
import { useState } from 'react'
import {
  VerifyKitProvider, Viewer, useVerification, defaultLayoutPlugin,
} from '@trexolab/verifykit-react'
import '@trexolab/verifykit-react/styles.css'
 
function Inner() {
  const verification = useVerification()
  const [layout] = useState(() => defaultLayoutPlugin())
 
  return (
    <div style={{ height: '100vh' }}>
      <Viewer
        fileBuffer={verification.fileBuffer ?? undefined}
        fileName={verification.fileName}
        plugins={[layout.plugin]}
        onOpenFile={(file) => verification.load(file)}
        signatures={verification.signatures}
        unsignedFields={verification.unsignedFields}
        verificationStatus={verification.status ?? undefined}
        verifying={verification.isLoading}
      />
    </div>
  )
}
 
export default function Page() {
  return (
    <VerifyKitProvider
      config={{ workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs' }}
    >
      <Inner />
    </VerifyKitProvider>
  )
}

Both forms server-render and hydrate cleanly under Turbopack and webpack, in next dev and in production builds.

No canvas alias needed. VerifyKit renders through PDF.js's browser canvas and never imports the Node canvas package, so config.resolve.alias.canvas workarounds you may have copied from other PDF libraries are unnecessary.

Upgrading from ≤ 0.5.14: the viewer previously had to be wrapped in dynamic(() => import('./Viewer'), { ssr: false }), because importing it evaluated pdf.js at module scope and threw DOMMatrix is not defined under Node. You can now delete that wrapper — but leaving it in place still works, so the upgrade is not forced.

Keyboard Shortcuts

The following shortcuts are available when using defaultLayoutPlugin:

ShortcutActionPlugin
Ctrl+= / Ctrl++Zoom inzoomPlugin
Ctrl+-Zoom outzoomPlugin
Ctrl+0Fit pagezoomPlugin
PageDown / N / JNext pagepageNavigationPlugin
PageUp / P / KPrevious pagepageNavigationPlugin
R / Shift+RRotate CW / CCWrotationPlugin
Ctrl+FOpen searchsearchPlugin
Ctrl+PPrintprintPlugin
Ctrl+SDownloaddownloadPlugin
F5FullscreenfullscreenPlugin
H / SHand / Select toolselectionPlugin
?Keyboard shortcuts helpshortcutHelpPlugin

Other Components

ComponentDescription
<PasswordDialog>Password prompt for encrypted PDFs
<DocumentMessageBar>Top status bar showing verification result
<SignatureListPanel>Side panel listing all signatures
<SignaturePropertiesModal>Tabbed signature details modal (includes a PAdES tab for ETSI signatures)
<CertificateViewer>Certificate chain tree viewer

i18n

The React package exports translation utilities for internationalization:

ts
import { t, setLocale, registerLocale } from '@trexolab/verifykit-react'
 
// Get a translated string
const label = t('signature.valid')
 
// Switch locale at runtime
setLocale('fr')
 
// Register a custom locale
registerLocale('fr', { 'signature.valid': 'Signature valide', /* ... */ })

You can also pass locale and translations via the VerifyKitProvider config for initial setup.