Vanilla JS
@trexolab/verifykit-vanilla is a zero-build, drop-in PDF viewer with signature verification. No React knowledge required -- load from a CDN or install via npm and embed a full-featured PDF viewer in any web page.
The package bundles React internally and wraps @trexolab/verifykit-react to expose all viewer features as imperative JavaScript methods.
CDN Installation
Add the stylesheet and script tags to your HTML:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://verifykit.trexolab.com/cdn/verifykit.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://verifykit.trexolab.com/cdn/verifykit.umd.js"></script>
<script>
const viewer = VerifyKit.create(document.getElementById('viewer'), {
workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
theme: { mode: 'system' },
features: {
signatures: true,
print: true,
download: true,
search: true,
fullscreen: true,
},
})
fetch('/document.pdf')
.then(r => r.arrayBuffer())
.then(buf => viewer.load(buf, 'document.pdf'))
</script>
</body>
</html>The UMD bundle includes the WASM core. The
.wasmbinary file must be served from the same directory as the JS file. When using the CDN, this is handled automatically.
npm Installation
For bundler-based projects:
npm install @trexolab/verifykit-vanillaimport { create } from '@trexolab/verifykit-vanilla'
import '@trexolab/verifykit-vanilla/verifykit.css'
const viewer = create(document.getElementById('viewer'), {
workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
theme: { mode: 'system' },
features: {
signatures: true,
print: true,
download: true,
},
})
const result = await viewer.load(pdfArrayBuffer, 'report.pdf')
console.log('Signatures:', result.signatures.length)
viewer.destroy()Creating a Viewer
Use VerifyKit.create(container, options?) to mount a viewer on a DOM element:
const viewer = VerifyKit.create(document.getElementById('viewer'), {
workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
theme: { mode: 'system' },
})Container requirements: The container element must exist in the DOM and have explicit dimensions (width and height). The viewer fills its parent -- if the parent has zero height, nothing renders. A common pattern is style="width: 100%; height: 100vh;".
If the container is null or undefined, create() throws "requires a valid HTMLElement". Make sure the element exists before calling create(), or wrap the call in a DOMContentLoaded listener.
Loading Documents
Use viewer.load(source, fileName?) to load a PDF. It accepts three input types:
// URL string
await viewer.load('/documents/report.pdf', 'report.pdf')
// File object (e.g., from a file input)
const file = inputElement.files[0]
const buffer = await file.arrayBuffer()
await viewer.load(buffer, file.name)
// ArrayBuffer
const response = await fetch('/document.pdf')
const buf = await response.arrayBuffer()
await viewer.load(buf, 'document.pdf')load() returns a Promise<VerificationResult> containing all signature data. You can inspect the result immediately:
const result = await viewer.load(buffer, 'doc.pdf')
for (const sig of result.signatures) {
console.log(sig.name, sig.overallStatus)
}All imperative methods (like
goToPage,zoomIn) are no-ops untilload()has resolved. Alwaysawaitthe load before calling other methods.
Configuration Options
interface VerifyKitOptions {
// Worker (REQUIRED)
workerUrl: string // URL to the PDF.js worker script (required)
// Theme
theme?: {
mode?: 'light' | 'dark' | 'system'
overrides?: Record<string, string>
}
// Features
features?: VerifyKitFeatures
// Core config
trustStore?: TrustStoreConfig
plugins?: VerifyKitPlugin[]
// i18n
locale?: string
translations?: Partial<TranslationStrings>
// Callbacks
onVerified?: (result: VerificationResult) => void
onError?: (error: LoadError) => void
onDocumentLoaded?: (info: { pageCount: number; fileName: string }) => void
onPageChange?: (page: number) => void
onZoomChange?: (scale: number) => void
onThemeChange?: (mode: 'light' | 'dark') => void
}Instance Methods
Once a document is loaded, the viewer instance exposes a full programmatic API.
Navigation
viewer.goToPage(5) // Jump to page 5
viewer.goToNextPage() // Next page
viewer.goToPreviousPage() // Previous page
viewer.goToFirstPage() // First page
viewer.goToLastPage() // Last page
viewer.getCurrentPage() // Returns current page number
viewer.getPageCount() // Returns total page countZoom
viewer.zoomIn() // Zoom in one step
viewer.zoomOut() // Zoom out one step
viewer.zoomTo(2.0) // Set zoom to 200%
viewer.setFitMode('width') // Fit to width ('none', 'width', 'page')
viewer.getZoom() // Returns current zoom scaleRotation
viewer.rotateCW() // Rotate 90 degrees clockwise
viewer.rotateCCW() // Rotate 90 degrees counter-clockwise
viewer.getRotation() // Returns current rotation in degreesSearch
viewer.openSearch() // Open the search bar
viewer.closeSearch() // Close the search barPrint and Download
viewer.print() // Open print dialog
viewer.download() // Download the PDFFullscreen
viewer.enterFullscreen() // Enter fullscreen mode
viewer.exitFullscreen() // Exit fullscreen mode
viewer.toggleFullscreen() // Toggle fullscreenSignature Panel
viewer.openSignaturePanel() // Open the signature details panel
viewer.closeSignaturePanel()// Close the signature panelTheme
viewer.setTheme('dark') // Set theme ('light', 'dark', 'system')Revalidation
viewer.revalidate() // Re-verify all signatures on the current bufferState Inspection
viewer.getVerificationResult() // Returns VerificationResult or null
viewer.getSignatures() // Returns PdfSignature[]Event Handling
Subscribe to viewer events using viewer.on(event, callback). Each call returns an unsubscribe function.
// Verification complete
viewer.on('verified', (result) => {
console.log('Signatures:', result.signatures.length)
})
// Document loaded
viewer.on('documentLoaded', (info) => {
console.log(`Loaded ${info.pageCount} pages: ${info.fileName}`)
})
// Page navigation
const unsub = viewer.on('pageChange', (page) => {
console.log('Current page:', page)
})
// Zoom changes
viewer.on('zoomChange', (scale) => {
console.log('Zoom:', Math.round(scale * 100) + '%')
})
// Theme changes
viewer.on('themeChange', (mode) => {
console.log('Theme:', mode)
})
// Rotation
viewer.on('rotationChange', (rotation) => {
console.log('Rotation:', rotation)
})
// Fullscreen
viewer.on('fullscreenChange', (isFullscreen) => {
console.log('Fullscreen:', isFullscreen)
})
// Error (receives a LoadError object with name and message)
viewer.on('error', (error) => {
console.error('Viewer error:', error.name, error.message)
})
// Unsubscribe
unsub()Feature Flags
Toggle individual viewer features via the features option. Each feature defaults to the value shown:
interface VerifyKitFeatures {
search?: boolean // default: true
print?: boolean // default: true
download?: boolean // default: false
signatures?: boolean // default: true
thumbnails?: boolean // default: true
fullscreen?: boolean // default: true
themeToggle?: boolean // default: true
openFile?: boolean // default: false
rotation?: boolean // default: true
contextMenu?: boolean // default: true
properties?: boolean // default: true
shortcuts?: boolean // default: true
highlights?: boolean // default: true
}Example -- enable download and file opening, disable print:
const viewer = VerifyKit.create(el, {
workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
features: {
download: true,
openFile: true,
print: false,
},
})Internally, features flags are mapped to defaultLayoutPlugin({ disable: {...} }).
With File Input
A common pattern using a standard HTML file input:
<input type="file" id="file-input" accept=".pdf" />
<div id="viewer" style="width: 100%; height: 80vh;"></div>
<script src="https://verifykit.trexolab.com/cdn/verifykit.umd.js"></script>
<script>
const viewer = VerifyKit.create(document.getElementById('viewer'), {
workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
})
document.getElementById('file-input').addEventListener('change', (e) => {
const file = e.target.files[0]
if (!file) return
file.arrayBuffer().then(buf => viewer.load(buf, file.name))
})
</script>With Core Plugins
Core verification plugins (like revocation checking) can be passed through to the WASM engine:
import { create } from '@trexolab/verifykit-vanilla'
import { revocationPlugin } from '@trexolab/verifykit-plugin-revocation'
const viewer = create(document.getElementById('viewer'), {
workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
plugins: [revocationPlugin()],
trustStore: {
certificates: [myRootCaPem],
mode: 'merge',
},
})
const result = await viewer.load(pdfBuffer, 'document.pdf')
// Revocation checks now include live CRL/OCSP lookupsSelf-Hosting
When self-hosting instead of using a CDN, copy the entire dist/ directory:
cp -r node_modules/@trexolab/verifykit-vanilla/dist/ public/verifykit/Then reference the files from your HTML:
<link rel="stylesheet" href="/verifykit/verifykit.css">
<script src="/verifykit/verifykit.umd.js"></script>The WASM binary is resolved relative to the JS file's URL. As long as both files are served from the same directory, loading is automatic.
Cleanup
Always call destroy() when the viewer is no longer needed. This cleans up store subscriptions, event listeners, and worker blob URLs:
viewer.destroy()In single-page applications, call destroy() when the viewer's container is removed from the DOM (e.g., on route change or component unmount).