Integrations
Vanilla JS integration
Install one package, import it, and mount the viewer. The render engine, its worker, and (for the framework wrappers) React all come bundled — nothing else to configure.
01 / Live preview
Open the full interactive demo →This is the actual Vanilla JS app built from the code below, running live in an iframe.
02 / Install
npm install @trupdf/viewer react react-dom03 / Example
// No worker setup needed — @trupdf/viewer ships a self-contained render worker.
//
// The viewer chrome is React. With no framework wrapper, we mount it ourselves
// with react-dom — this is exactly what @trupdf/vue|angular|svelte's
// `mountTruPDF` does internally. (Use those wrappers instead if you're on a
// framework; this no-framework path is why react + react-dom are installed here.)
import { createElement } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { DocViewer, TruPDFSDK, type DocViewerProps, type ViewerEngine } from '@trupdf/viewer';
import '@trupdf/viewer/styles.css';
const hostEl = document.getElementById('viewer')!;
const statusEl = document.getElementById('status')!;
// The SDK is the public, framework-agnostic control surface. Attach it to
// the engine once the viewer hands one over, then drive everything through
// typed events + methods (never touch internal classes).
const sdk = new TruPDFSDK();
sdk.events.on('document.loaded', ({ numPages }) => {
statusEl.textContent = `loaded · ${numPages} pages`;
});
sdk.events.on('page.changed', ({ page }) => {
statusEl.textContent = `page ${page}`;
});
sdk.events.on('document.error', ({ error }) => {
statusEl.textContent = `error: ${error.message}`;
});
// --- tiny mount helper (createRoot + render) ---
const root: Root = createRoot(hostEl);
function render(props: DocViewerProps) {
root.render(createElement(DocViewer, props));
}
let currentSource: DocViewerProps['source'] = '/sample.pdf';
function show(source: DocViewerProps['source']) {
currentSource = source;
render({
source,
theme: 'system',
// engineRef gives us the engine; hand it to the SDK.
engineRef: (engine: ViewerEngine | null) => {
if (engine) void sdk.attach(engine);
},
});
}
// File open via the header button. Navigation, zoom, search, annotations etc.
// are all provided by the viewer's own toolbar — no need to wire them here.
document.getElementById('file')!.addEventListener('change', (e) => {
const file = (e.target as HTMLInputElement).files?.[0];
if (file) file.arrayBuffer().then(show);
});
show(currentSource);
// Clean up on full-page teardown (HMR / navigation).
window.addEventListener('beforeunload', () => {
void sdk.destroy();
root.unmount();
});