VERIPDFSDK

VeriPDF Examples

Practical examples covering common use cases. Each example is self-contained and can be copied directly into your project.

Table of Contents


Basic React Viewer

Full-featured viewer with all plugins enabled, drag-and-drop file loading, and verification display.

import { useState, useCallback, useRef } from 'react'
import {
  VeriPdfProvider,
  Viewer,
  WelcomeScreen,
  useVerification,
  defaultLayoutPlugin,
  type ViewerHandle,
} from '@trexolab/verifykit-react'
import '@trexolab/verifykit-react/styles.css'
 
function App() {
  return (
    <VeriPdfProvider config={{ theme: { mode: 'system' } }}>
      <div style={{ height: '100vh' }}>
        <PdfApp />
      </div>
    </VeriPdfProvider>
  )
}
 
function PdfApp() {
  const verification = useVerification()
  const viewerRef = useRef<ViewerHandle>(null)
  const [layout] = useState(() => defaultLayoutPlugin())
 
  const handleFile = useCallback(
    async (file: File) => {
      try {
        await verification.load(file)
      } catch (err) {
        console.error('Failed to load PDF:', err)
      }
    },
    [verification],
  )
 
  if (!verification.fileBuffer) {
    return (
      <WelcomeScreen
        onOpenFile={handleFile}
        title="My PDF Viewer"
        subtitle="Drag and drop a PDF here, or click to browse"
      />
    )
  }
 
  return (
    <Viewer
      ref={viewerRef}
      fileBuffer={verification.fileBuffer}
      fileName={verification.fileName}
      plugins={[layout.plugin]}
      onOpenFile={handleFile}
      initialState={{
        signatures: verification.signatures,
        unsignedFields: verification.unsignedFields,
        verificationStatus: verification.status ?? undefined,
      }}
    />
  )
}

What you get: toolbar, sidebar (thumbnails/bookmarks/attachments), search, zoom, page navigation, rotation, print, download, fullscreen, theme toggle, signature panel, keyboard shortcuts, context menu, and drag-and-drop file loading.

To disable specific features:

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

Headless Verification (Node.js)

Server-side signature verification with no UI. Prints all 8 check results and certificate details.

import { createVerifier } from '@trexolab/verifykit-core'
import { readFile } from 'node:fs/promises'
 
async function main() {
  // createVerifier() is async -- initializes the WASM module
  const verifier = await createVerifier()
 
  try {
    const buffer = await readFile('signed-contract.pdf')
    const result = await verifier.verify(buffer, 'signed-contract.pdf')
 
    console.log(`File: ${result.fileName}`)
    console.log(`Size: ${result.fileSize} bytes`)
    console.log(`Hash: ${result.fileHash}`)
    console.log(`Signatures: ${result.signatures.length}`)
    console.log()
 
    for (const sig of result.signatures) {
      console.log(`--- Signature: ${sig.name} ---`)
      console.log(`  Status:      ${sig.overallStatus}`)
      console.log(`  Message:     ${sig.overallMessage}`)
      console.log(`  PAdES Level: ${sig.padesLevel}`)
      console.log(`  Integrity:   ${sig.integrityCheck.status} -- ${sig.integrityCheck.detail}`)
      console.log(`  Signature:   ${sig.signatureCheck.status} -- ${sig.signatureCheck.detail}`)
      console.log(`  Chain:       ${sig.certificateChainCheck.status} -- ${sig.certificateChainCheck.detail}`)
      console.log(`  Expiry:      ${sig.expiryCheck.status} -- ${sig.expiryCheck.detail}`)
      console.log(`  Timestamp:   ${sig.timestampCheck.status} -- ${sig.timestampCheck.detail}`)
      console.log(`  Revocation:  ${sig.revocationCheck.status} -- ${sig.revocationCheck.detail}`)
      console.log(`  Algorithm:   ${sig.algorithmCheck.status} -- ${sig.algorithmCheck.detail}`)
      console.log(`  EKU:         ${sig.ekuCheck.status} -- ${sig.ekuCheck.detail}`)
 
      if (sig.signerCertificate) {
        console.log(`  Signer:      ${sig.signerCertificate.subject}`)
        console.log(`  Issuer:      ${sig.signerCertificate.issuer}`)
        console.log(`  Valid:       ${sig.signerCertificate.notBefore.toISOString()} to ${sig.signerCertificate.notAfter.toISOString()}`)
        console.log(`  Fingerprint: ${sig.signerCertificate.fingerprint}`)
        console.log(`  Key:         ${sig.signerCertificate.keyAlgorithm} ${sig.signerCertificate.keySize ?? ''}`)
      }
 
      if (sig.timestamp) {
        console.log(`  TSA:         ${sig.timestamp.tsaName}`)
        console.log(`  TSA Time:    ${sig.timestamp.time.toISOString()}`)
      }
 
      console.log()
    }
  } catch (err) {
    console.error('Verification failed:', err instanceof Error ? err.message : err)
    process.exitCode = 1
  } finally {
    verifier.dispose()
  }
}
 
main()

For one-shot verification without managing a verifier instance:

import { verifyPdf } from '@trexolab/verifykit-core'
import { readFile } from 'node:fs/promises'
 
const buffer = await readFile('document.pdf')
const result = await verifyPdf(buffer, 'document.pdf')
 
for (const sig of result.signatures) {
  console.log(`${sig.name}: ${sig.overallStatus}`)
}

Vanilla JS Drop-in

CDN (No Build Tools)

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://unpkg.com/@trexolab/verifykit-vanilla/dist/veripdf.css">
  <style>
    html, body { height: 100%; margin: 0; padding: 0; }
  </style>
</head>
<body>
  <div id="viewer" style="width: 100%; height: 100vh;"></div>
 
  <script src="https://unpkg.com/@trexolab/verifykit-vanilla/dist/veripdf.umd.js"></script>
  <script>
    var viewer = VeriPdf.create(document.getElementById('viewer'), {
      theme: { mode: 'system' },
      features: {
        signatures: true,
        print: true,
        download: true,
        search: true,
        fullscreen: true,
        themeToggle: true,
      },
      onError: function (errMsg) {
        console.error('VeriPdf error:', errMsg)
      },
    })
 
    fetch('/document.pdf')
      .then(function (r) { return r.arrayBuffer() })
      .then(function (buf) { return viewer.load(buf, 'document.pdf') })
      .then(function (result) {
        console.log('Signatures:', result.signatures.length)
        for (var i = 0; i < result.signatures.length; i++) {
          console.log(result.signatures[i].name + ': ' + result.signatures[i].overallStatus)
        }
      })
      .catch(function (err) {
        console.error('Failed to load PDF:', err)
      })
  </script>
</body>
</html>

With File Input

<input type="file" id="file-input" accept=".pdf" />
<div id="viewer" style="width: 100%; height: 80vh;"></div>
 
<script src="https://unpkg.com/@trexolab/verifykit-vanilla/dist/veripdf.umd.js"></script>
<script>
  var viewer = VeriPdf.create(document.getElementById('viewer'), {
    onError: function (errMsg) { alert('Error: ' + errMsg) },
  })
 
  document.getElementById('file-input').addEventListener('change', function (e) {
    var file = e.target.files[0]
    if (!file) return
 
    file.arrayBuffer()
      .then(function (buf) { return viewer.load(buf, file.name) })
      .then(function (result) {
        console.log('Loaded:', result.signatures.length, 'signatures')
      })
      .catch(function (err) {
        console.error('Failed to load file:', err)
      })
  })
</script>

ESM Import with Programmatic API

import { create } from '@trexolab/verifykit-vanilla'
import '@trexolab/verifykit-vanilla/veripdf.css'
 
const viewer = create(document.getElementById('viewer')!, {
  theme: { mode: 'dark' },
  features: { signatures: true, print: true, download: true },
})
 
// Load a document
const result = await viewer.load(pdfArrayBuffer, 'report.pdf')
console.log(`Verified ${result.signatures.length} signatures`)
 
// Navigate
viewer.goToPage(5)
viewer.goToNextPage()
console.log('Page:', viewer.getCurrentPage(), '/', viewer.getPageCount())
 
// Zoom
viewer.zoomIn()
viewer.zoomTo(2.0)
viewer.setFitMode('width')
 
// Subscribe to events
const unsubPage = viewer.on('pageChange', (page) => {
  console.log('Now on page:', page)
})
 
const unsubVerified = viewer.on('verified', (result) => {
  console.log('Verification complete:', result)
})
 
// Unsubscribe when done
unsubPage()
unsubVerified()
 
// Clean up when removing
viewer.destroy()

Custom Trust Store

Add Enterprise Root CAs (Merge with Built-in)

The @trexolab/verifykit-core engine ships with 119 embedded AATL root certificates. You can add your own:

import { createVerifier } from '@trexolab/verifykit-core'
import { readFile } from 'node:fs/promises'
 
const enterpriseRootPem = await readFile('enterprise-root-ca.pem', 'utf-8')
 
// 'merge' adds your cert alongside the built-in 119 AATL certificates
const verifier = await createVerifier({
  trustStore: {
    certificates: [enterpriseRootPem],
    mode: 'merge',
  },
})
 
try {
  const pdfBuffer = await readFile('document.pdf')
  const result = await verifier.verify(pdfBuffer, 'document.pdf')
  for (const sig of result.signatures) {
    console.log(`${sig.name}: ${sig.overallStatus}`)
  }
} finally {
  verifier.dispose()
}

Replace the Built-in Store Entirely

// Only your certificate is trusted -- built-in AATL store is ignored
const verifier = await createVerifier({
  trustStore: {
    certificates: [enterpriseRootPem],
    mode: 'replace',
  },
})

Custom Trust Store in React

<VeriPdfProvider config={{
  trustStore: {
    certificates: [myCompanyRootCaPem],
    mode: 'merge',
  },
}}>
  <PdfApp />
</VeriPdfProvider>

Custom Trust Store in Vanilla

const viewer = VeriPdf.create(document.getElementById('viewer'), {
  trustStore: {
    certificates: [enterpriseRootPem],
    mode: 'merge',
  },
})

Online Revocation Checking

Node.js

import { createVerifier } from '@trexolab/verifykit-core'
import { revocationPlugin } from '@trexolab/verifykit-plugin-revocation'
import { readFile } from 'node:fs/promises'
 
async function main() {
  const verifier = await createVerifier({
    plugins: [revocationPlugin({ timeout: 15_000 })],
  })
 
  try {
    const buffer = await readFile('document.pdf')
    const result = await verifier.verify(buffer, 'document.pdf')
 
    for (const sig of result.signatures) {
      // Without the plugin: "Not checked (offline)"
      // With the plugin: live CRL/OCSP lookup result
      console.log(`Revocation: ${sig.revocationCheck.status} -- ${sig.revocationCheck.detail}`)
    }
  } finally {
    verifier.dispose()
  }
}
 
main()

Disable CRL (OCSP Only)

const verifier = await createVerifier({
  plugins: [revocationPlugin({ ocsp: true, crl: false })],
})

Vanilla API with Revocation

import { create } from '@trexolab/verifykit-vanilla'
import { revocationPlugin } from '@trexolab/verifykit-plugin-revocation'
import '@trexolab/verifykit-vanilla/veripdf.css'
 
const viewer = create(document.getElementById('viewer')!, {
  plugins: [revocationPlugin()],
  trustStore: {
    certificates: [enterpriseRootPem],
    mode: 'merge',
  },
})
 
const result = await viewer.load(buffer, 'contract.pdf')
for (const sig of result.signatures) {
  console.log(`Revocation: ${sig.revocationCheck.status} -- ${sig.revocationCheck.detail}`)
}

Metadata Extraction

Node.js

Extract document properties without performing signature verification:

import { createVerifier } from '@trexolab/verifykit-core'
import { readFile } from 'node:fs/promises'
 
async function main() {
  const verifier = await createVerifier()
 
  try {
    const buffer = await readFile('document.pdf')
    const { metadata, permissions } = await verifier.extractMetadata(buffer)
 
    console.log('Title:', metadata.title)
    console.log('Author:', metadata.author)
    console.log('Creator:', metadata.creator)
    console.log('Producer:', metadata.producer)
    console.log('Pages:', metadata.pageCount)
    console.log('PDF Version:', metadata.pdfVersion)
    console.log('Created:', metadata.creationDate?.toISOString())
    console.log('Modified:', metadata.modDate?.toISOString())
 
    console.log('\nPermissions:')
    console.log('  Encrypted:', permissions.encrypted)
    console.log('  Printing:', permissions.printing)
    console.log('  Copying:', permissions.copying)
    console.log('  Annotations:', permissions.annotations)
    console.log('  Form filling:', permissions.formFilling)
  } finally {
    verifier.dispose()
  }
}
 
main()

One-Shot Metadata Extraction

import { extractPdfMetadata } from '@trexolab/verifykit-core'
import { readFile } from 'node:fs/promises'
 
const buffer = await readFile('document.pdf')
const { metadata, permissions } = await extractPdfMetadata(buffer)
console.log(`${metadata.title} by ${metadata.author} (${metadata.pageCount} pages)`)

React Metadata Display

import { useVerification } from '@trexolab/verifykit-react'
 
function MetadataPanel() {
  const verification = useVerification()
 
  if (!verification.metadata) return null
 
  const { metadata, permissions } = verification
 
  return (
    <div>
      <h3>Document Properties</h3>
      <p>Title: {metadata.title || '(none)'}</p>
      <p>Author: {metadata.author || '(none)'}</p>
      <p>Pages: {metadata.pageCount}</p>
      <p>PDF Version: {metadata.pdfVersion}</p>
      <p>Encrypted: {permissions?.encrypted ? 'Yes' : 'No'}</p>
    </div>
  )
}

Certificate Inspection

import { createVerifier, oidToName, parseDn } from '@trexolab/verifykit-core'
import { readFile } from 'node:fs/promises'
 
const verifier = await createVerifier()
 
try {
  const buffer = await readFile('signed.pdf')
  const result = await verifier.verify(buffer, 'signed.pdf')
 
  for (const sig of result.signatures) {
    console.log(`\n=== ${sig.name} ===`)
    console.log(`Certificate chain depth: ${sig.certificateChain.length}`)
 
    for (let i = 0; i < sig.certificateChain.length; i++) {
      const cert = sig.certificateChain[i]
      const indent = '  '.repeat(i)
      console.log(`${indent}[${i}] ${parseDn(cert.subject)}`)
      console.log(`${indent}    Issuer: ${parseDn(cert.issuer)}`)
      console.log(`${indent}    Serial: ${cert.serialNumber}`)
      console.log(`${indent}    Valid: ${cert.notBefore.toISOString()} to ${cert.notAfter.toISOString()}`)
      console.log(`${indent}    Expired: ${cert.isExpired}`)
      console.log(`${indent}    CA: ${cert.isCA}`)
      console.log(`${indent}    Key: ${cert.keyAlgorithm} ${cert.keySize ?? ''}`)
      console.log(`${indent}    Sig Alg: ${oidToName(cert.signatureAlgorithm)}`)
      console.log(`${indent}    Fingerprint (SHA-256): ${cert.fingerprint}`)
      if (cert.keyUsage?.length) {
        console.log(`${indent}    Key Usage: ${cert.keyUsage.join(', ')}`)
      }
      if (cert.extKeyUsage?.length) {
        console.log(`${indent}    Ext Key Usage: ${cert.extKeyUsage.join(', ')}`)
      }
      if (cert.crlDistributionPoints?.length) {
        console.log(`${indent}    CRL URLs: ${cert.crlDistributionPoints.join(', ')}`)
      }
      if (cert.ocspUrls?.length) {
        console.log(`${indent}    OCSP URLs: ${cert.ocspUrls.join(', ')}`)
      }
    }
  }
} finally {
  verifier.dispose()
}

Export Certificates in React

import {
  useVerification,
  exportCertAsPem,
  exportCertAsDer,
  downloadBlob,
  type PdfSignature,
} from '@trexolab/verifykit-react'
 
function CertExporter() {
  const verification = useVerification()
 
  function exportPem(sig: PdfSignature) {
    if (!sig.signerCertificate?.rawDer) return
    try {
      const pem = exportCertAsPem(sig.signerCertificate)
      const blob = new Blob([pem], { type: 'application/x-pem-file' })
      downloadBlob(blob, `${sig.name}.pem`)
    } catch (err) {
      console.error('PEM export failed:', err)
    }
  }
 
  function exportDer(sig: PdfSignature) {
    if (!sig.signerCertificate?.rawDer) return
    try {
      const der = exportCertAsDer(sig.signerCertificate)
      const blob = new Blob([der], { type: 'application/x-x509-ca-cert' })
      downloadBlob(blob, `${sig.name}.cer`)
    } catch (err) {
      console.error('DER export failed:', err)
    }
  }
 
  return (
    <div>
      {verification.signatures.map((sig, i) => (
        <div key={i}>
          <span>{sig.name}</span>
          <button onClick={() => exportPem(sig)}>Export PEM</button>
          <button onClick={() => exportDer(sig)}>Export DER</button>
        </div>
      ))}
    </div>
  )
}

Theme Customization

Set Theme Mode

// 'system' follows the OS preference, or pick 'light' / 'dark' explicitly
<VeriPdfProvider config={{
  theme: { mode: 'dark' },
}}>

Override CSS Variables

<VeriPdfProvider config={{
  theme: {
    mode: 'light',
    overrides: {
      '--primary': '#6366f1',
      '--primary-hover': '#4f46e5',
      '--bg': '#fefefe',
      '--toolbar-bg': '#f8f8f8',
    },
  },
}}>

Toggle Theme at Runtime (Plugin API)

const [layout] = useState(() => defaultLayoutPlugin())
 
// Toggle between light and dark
layout.theme.toggleTheme()
 
// Set a specific theme
layout.theme.setTheme('dark')
 
// Get current theme
const current = layout.theme.getTheme() // 'light' | 'dark'

Toggle Theme at Runtime (Vanilla API)

const viewer = VeriPdf.create(container, { theme: { mode: 'system' } })
 
// Change theme after creation
viewer.setTheme('dark')
viewer.setTheme('light')
viewer.setTheme('system')

Vanilla Theme Change Events

viewer.on('themeChange', (mode) => {
  document.body.className = String(mode)
})

Required CSS Reset

Your app needs this so the viewer fills the viewport:

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

Plugin Development

Writing a Custom Viewer Plugin

This example creates a plugin that counts words across all pages and exposes the count via a toolbar slot and keyboard shortcut.

import { useState } from 'react'
import type { ViewerPlugin, PluginContext } from '@trexolab/verifykit-react'
 
interface WordCountPluginApi {
  getWordCount(): number
}
 
function wordCountPlugin(): { plugin: ViewerPlugin; api: WordCountPluginApi } {
  let ctx: PluginContext
  let wordCount = 0
 
  const plugin: ViewerPlugin = {
    name: 'word-count',
 
    install(c) {
      ctx = c
 
      // Register a keyboard shortcut
      ctx.registerShortcut({
        id: 'word-count-show',
        key: 'w',
        ctrl: true,
        shift: true,
        description: 'Show word count',
        handler(e) {
          e.preventDefault()
          alert(`Approximate word count: ${wordCount}`)
        },
      })
    },
 
    async onDocumentLoad({ document, numPages }) {
      wordCount = 0
      try {
        for (let i = 1; i <= numPages; i++) {
          const page = await document.getPage(i)
          const textContent = await page.getTextContent()
          for (const item of textContent.items) {
            if ('str' in item) {
              wordCount += item.str.split(/\s+/).filter(Boolean).length
            }
          }
        }
      } catch (err) {
        console.error('Word count failed:', err)
      }
    },
 
    // Add a toolbar slot
    renderToolbarSlot: {
      WordCount: () => (
        <span style={{ fontSize: 11, padding: '0 8px', opacity: 0.7 }}>
          ~{wordCount.toLocaleString()} words
        </span>
      ),
    },
 
    destroy() {
      wordCount = 0
    },
  }
 
  const api: WordCountPluginApi = {
    getWordCount: () => wordCount,
  }
 
  return { plugin, api }
}
 
// Usage:
function App() {
  const [wc] = useState(() => wordCountPlugin())
  const [layout] = useState(() => defaultLayoutPlugin())
 
  return (
    <Viewer
      fileBuffer={buffer}
      plugins={[layout.plugin, wc.plugin]}
    />
  )
}

Plugin Lifecycle

1. Factory called:          const wc = wordCountPlugin()
2. Passed to Viewer:        <Viewer plugins={[wc.plugin]} />
3. install() called:        plugin receives PluginContext (store, shortcuts, etc.)
4. Document loads:          onDocumentLoad({ document, numPages })
5. User interacts:          toolbar slots render, shortcuts fire
6. Document unloads:        onDocumentUnload()
7. Viewer unmounts:         destroy() called for cleanup

Writing a Core Verification Plugin

Core plugins extend the verification engine (not the viewer UI):

import type { VeriPdfPlugin, CertificateInfo, RevocationCheckResult } from '@trexolab/verifykit-core'
 
function myRevocationPlugin(): VeriPdfPlugin {
  return {
    name: 'my-revocation',
    revocation: {
      async checkOCSP(cert: CertificateInfo, issuer: CertificateInfo, urls: string[]): Promise<RevocationCheckResult> {
        // Your custom OCSP logic here
        for (const url of urls) {
          try {
            const response = await fetch(url, { method: 'POST', body: buildOcspRequest(cert) })
            // Parse response...
            return { status: 'good', source: `custom OCSP (${url})` }
          } catch {
            continue
          }
        }
        return { status: 'unknown', source: 'custom OCSP (all failed)' }
      },
    },
  }
}

Next.js Integration

Client Component Wrapper

// components/PdfViewer.tsx
'use client'
 
import { useState, useCallback, useRef } from 'react'
import {
  VeriPdfProvider,
  Viewer,
  WelcomeScreen,
  useVerification,
  defaultLayoutPlugin,
  type ViewerHandle,
} from '@trexolab/verifykit-react'
import '@trexolab/verifykit-react/styles.css'
 
export function PdfViewerApp() {
  return (
    <VeriPdfProvider config={{ theme: { mode: 'system' } }}>
      <PdfViewerInner />
    </VeriPdfProvider>
  )
}
 
function PdfViewerInner() {
  const verification = useVerification()
  const viewerRef = useRef<ViewerHandle>(null)
  const [layout] = useState(() => defaultLayoutPlugin())
 
  const handleFile = useCallback(
    async (file: File) => {
      await 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}
      initialState={{
        signatures: verification.signatures,
        verificationStatus: verification.status ?? undefined,
      }}
    />
  )
}

Dynamic Import in Page

// app/pdf/page.tsx
import dynamic from 'next/dynamic'
 
const PdfViewerApp = dynamic(
  () => import('@/components/PdfViewer').then(m => m.PdfViewerApp),
  { ssr: false, loading: () => <p>Loading viewer...</p> }
)
 
export default function PdfPage() {
  return (
    <div style={{ height: '100vh' }}>
      <PdfViewerApp />
    </div>
  )
}

Next.js WASM Configuration

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  webpack: (config) => {
    config.experiments = { ...config.experiments, asyncWebAssembly: true }
    return config
  },
}
 
module.exports = nextConfig

Server-Side Verification (Route Handler)

You can also use @trexolab/verifykit-core in a Next.js API route for server-side verification:

// app/api/verify/route.ts
import { createVerifier } from '@trexolab/verifykit-core'
 
export async function POST(request: Request) {
  const formData = await request.formData()
  const file = formData.get('pdf') as File
  if (!file) {
    return Response.json({ error: 'No file provided' }, { status: 400 })
  }
 
  const verifier = await createVerifier()
 
  try {
    const buffer = await file.arrayBuffer()
    const result = await verifier.verify(buffer, file.name)
    return Response.json(result)
  } finally {
    verifier.dispose()
  }
}

Error Handling Patterns

React Error Handling

import { VeriPdfProvider, useVerification } from '@trexolab/verifykit-react'
import '@trexolab/verifykit-react/styles.css'
 
function PdfApp() {
  const verification = useVerification()
 
  async function handleFile(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0]
    if (!file) return
 
    try {
      await verification.load(file)
    } catch (err) {
      // This catches errors during PDF loading and WASM initialization
      console.error('Load failed:', err instanceof Error ? err.message : err)
    }
  }
 
  return (
    <div>
      <input type="file" accept=".pdf" onChange={handleFile} />
 
      {/* Loading state */}
      {verification.isLoading && <p>Verifying signatures...</p>}
 
      {/* Error state -- set by the hook after load() fails */}
      {verification.error && (
        <div style={{ color: 'red', padding: 16 }}>
          <strong>Error:</strong> {verification.error}
        </div>
      )}
 
      {/* Success state */}
      {verification.signatures.map((sig, i) => (
        <div key={i}>
          <strong>{sig.name}:</strong> {sig.overallStatus} -- {sig.overallMessage}
        </div>
      ))}
    </div>
  )
}

Node.js Error Handling

import { createVerifier } from '@trexolab/verifykit-core'
import { readFile } from 'node:fs/promises'
 
async function verifyWithRetry(path: string, maxRetries = 2) {
  let verifier = await createVerifier()
  let lastError: Error | null = null
 
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      const buffer = await readFile(path)
      const result = await verifier.verify(buffer, path)
      return result
    } catch (err) {
      lastError = err instanceof Error ? err : new Error(String(err))
      console.warn(`Attempt ${attempt + 1} failed: ${lastError.message}`)
 
      // Dispose and recreate verifier on failure
      verifier.dispose()
      if (attempt < maxRetries) {
        verifier = await createVerifier()
      }
    }
  }
 
  throw lastError
}

Vanilla Error Handling

const viewer = VeriPdf.create(container, {
  onError: (errMsg) => {
    // Called for any error during the viewer lifecycle
    showErrorBanner(errMsg)
  },
})
 
// load() returns a Promise -- handle both async and sync errors
try {
  const result = await viewer.load(buffer, 'doc.pdf')
  console.log('Verified successfully:', result.signatures.length, 'signatures')
} catch (err) {
  // Catches load-time errors (bad PDF, WASM failure, network errors)
  console.error('Load failed:', err instanceof Error ? err.message : err)
}
 
// Subscribe to error events
viewer.on('error', (errMsg) => {
  console.error('Viewer error event:', errMsg)
})

Common Error Scenarios

ErrorCauseRecovery
Failed to fetch PDF: HTTP 404URL input returned a 404Check the URL, ensure CORS headers are set
Unsupported input typeInvalid argument to load() or verify()Pass ArrayBuffer, Uint8Array, File, or URL string
WASM module not initializedSynchronous function called before WASM loadedAwait createVerifier() or initWasm() first
VeriPdf viewer failed to initialize within 10 secondsContainer not in DOM or WASM load failedEnsure container is attached and visible; check network for .wasm file
CompileError: WebAssembly.instantiate()Corrupted or missing .wasm binaryVerify the .wasm file is served with application/wasm MIME type