TPTruPDF

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.

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-dom

03 / 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();
});