npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@unilarva/pdfjs-viewer

v0.7.2

Published

Framework-independent, TypeScript-first browser PDF viewer built on Mozilla PDF.js, with a responsive default UI plus custom and headless modes.

Readme

@unilarva/pdfjs-viewer

A framework-independent, TypeScript-first browser PDF viewer built on Mozilla PDF.js. Use the complete responsive UI, bring your own controls, or embed just the document surface. Your application owns the integration; the viewer handles rendering, navigation, and interaction.

See it in use: Unilaiva songbook is the real-world application that started this project. It uses an application-owned UI, rather than the package's default theme.

Quick start | Usage guide | Customization

Highlights

  • Complete default UI, selectively generated controls, custom markup bindings, or a headless document surface. No framework adapter is required.
  • Responsive single-page and book-style spreads, touch and touchpad pinch zoom, drag-to-scroll, browser fullscreen, discrete-page presentation mode, and viewer-scoped keyboard shortcuts.
  • Search, filtered outlines, thumbnails, internal links, rotation, document metadata, attachments, optional-content layers, and cross-page text selection that survives view changes.
  • Visible-first, high-DPI rendering with configurable profiles that bound viewer-managed raster memory.
  • Read-only annotations and popups, interactive AcroForms and pure XFA forms, and filled-PDF export for supported form values. PDF JavaScript is never executed.
  • Controlled printing with ranges, paper sizes, two-page sheets, and DPI settings where the browser supports it, plus browser-source and application-adapter routes.
  • URL and in-memory loading, password handling, reusable viewers, shared workers, typed state and events, document-local Back/Forward, and optional URL-hash navigation.
  • Themeable, localizable controls with semantic markup, focus management, and reduced-motion support. See the accessibility limitations when evaluating PDF content for assistive technologies.
  • Deterministic server-renderable UI and a browser DOM factory that avoids HTML parsing sinks for CSP and Trusted Types-conscious deployments.

Pre-1.0 status: Public APIs, options, behavior, styling hooks, and the exact supported pdfjs-dist version set can change between minor releases. Pin exact versions, review release notes and peerDependencies before upgrading, and treat the peer dependency set, not this package version, as the PDF.js compatibility contract.

Guides

  • Usage guide: loading, lifecycle, options, profiles, public API, events, behavior, printing, security, and troubleshooting.
  • Customization guide: default, custom, and headless UI modes; markup bindings; styling; generated UI; and localization.
  • Architecture: maintainer-only source, release-qualification, and implementation guidance.

Requirements

  • A modern browser with ESM, private class fields, workers, AbortController, and standard DOM APIs. Distributed JavaScript targets ES2022.
  • Current desktop and mobile Chromium, Firefox, and Safari/WebKit are supported. Automated mobile projects use desktop engine binaries with a touch-capable narrow viewport; they are interaction regressions, not physical Android/iOS qualification. Native selection handles and system print UI remain browser-owned.
  • The consuming application must install the exact pdfjs-dist version declared in this release's peerDependencies (currently 6.3.289). The package does not bundle PDF.js, its worker, fonts, CMaps, ICC data, or WASM assets.
  • An ESM-capable bundler or browser setup. There is no CommonJS build.
  • A browser document when constructing a viewer. SSR tooling can import the package, but viewer, worker, and browser UI creation must run client-side after the host exists.

| Import | Purpose | | --------------------------------------- | ----------------------------------------------------------------------------------------- | | @unilarva/pdfjs-viewer | Browser ESM runtime, utilities, and public TypeScript contracts. | | @unilarva/pdfjs-viewer/core.css | Functional viewer, text, annotation, XFA, and print CSS for custom/headless UI. | | @unilarva/pdfjs-viewer/default-ui.css | Functional CSS plus the complete generated default theme. Do not combine with core.css. | | @unilarva/pdfjs-viewer/package.json | Metadata for build tooling and compatibility checks, not a runtime module. |

Only documented entry points are public. Implementation modules below dist/ are not.

Quick Start

Install the viewer and its exact PDF.js peer:

npm install --save-exact @unilarva/pdfjs-viewer [email protected]

Give the host an explicit height; otherwise the scrollable document area can collapse:

<div id="pdf-viewer"></div>

<style>
  #pdf-viewer {
    width: 100%;
    height: 100vh;
    height: 100svh;
    min-height: 24rem;
  }
</style>

For a full-viewport viewer, svh avoids resizing with mobile browser chrome. Use dvh only when that resize is wanted. In an application shell, height: 100% works when every ancestor through the shell has an explicit height.

Copy node_modules/pdfjs-dist/build/pdf.worker.min.mjs to your application's public /assets/pdf.worker.min.mjs URL as part of the build, or use a bundler-specific worker recipe. Then configure the worker, create a runtime, and pass it to the viewer:

import { PdfjsViewer, PdfjsViewerRuntime } from "@unilarva/pdfjs-viewer";
import * as pdfjs from "pdfjs-dist/build/pdf.mjs";
import "@unilarva/pdfjs-viewer/default-ui.css";

const rootEl = document.querySelector<HTMLElement>("#pdf-viewer");
if (!rootEl) throw new Error("Missing #pdf-viewer");

const runtime = new PdfjsViewerRuntime({
  pdfjs,
  workerSrc: "/assets/pdf.worker.min.mjs",
});
const viewer = new PdfjsViewer({ rootEl, runtime });

const result = await viewer.load("/documents/example.pdf");
if (!result.ok && result.reason === "error") console.error(result.error);

window.addEventListener(
  "pagehide",
  () => {
    viewer.destroy();
    runtime.destroy(); // After its last attached viewer.
  },
  { once: true },
);

The omitted ui mode is the complete default UI. Keyboard shortcuts are scoped to events originating in the viewer; use keyboard: false to disable them. Import default-ui.css only for default UI. Custom and headless integrations import core.css only and provide their own shell styling.

The worker is intentionally not inferred, shipped, or downloaded. Keep it from the same installed pdfjs-dist tree as the display API. The usage guide worker recipes cover Vite, Webpack, static/CDN, and host-owned workers.

Next Integration Steps

Handle Readiness And Failure

Construct the viewer before starting a load so application state can subscribe to its root. Events are dispatched directly from rootEl; they do not bubble, cross shadow boundaries, or cancel. pdf:ready and a successful load() result both mean that the initial visible pages rendered.

rootEl.addEventListener("pdf:ready", () => {
  statusEl.textContent = "Ready";
});

rootEl.addEventListener("pdf:error", event => {
  const { error } = (event as CustomEvent<{ error: unknown }>).detail;
  console.error("PDF load failed", error);
  statusEl.textContent = "The PDF could not be opened.";
});

rootEl.addEventListener("pdf:statechange", event => {
  const state = (event as CustomEvent).detail;
  pageEl.textContent = `${state.currentPage} / ${state.pageCount}`;
});

load() returns an expected cancellation result when replaced, closed, destroyed, or cancelled by a password prompt. A load failure returns an error result and emits pdf:error. Invalid input and calls after destroy() throw or reject. See lifecycle and readiness, error handling, and events for the full contract.

Choose A UI Mode

The default UI is a responsive complete viewer. It is the shortest route to production and can be re-themed, localized, and composed with selected generated controls.

const viewer = new PdfjsViewer({
  rootEl,
  runtime,
  ui: {
    mode: "default",
    controls: { print: false },
  },
});

Use ui: "custom" to bind the document surface and only application-owned controls. Use ui: "headless" for a package-created surface driven completely through methods and events. Both require core.css, not default-ui.css. The customization guide documents their CSS, DOM ownership, accessibility, and localization contracts.

Configure Features Deliberately

Capabilities default to enabled, including search, text selection, outline, thumbnails, attachments, annotation links/markup, forms, layers, and native-print routing. Disable capabilities that the host does not expose, or configure them through their typed nested options:

const viewer = new PdfjsViewer({
  rootEl,
  runtime,
  pageLayout: "auto",
  fitMode: "auto",
  renderingProfile: "auto",
  features: {
    outline: { filter: true },
    thumbnails: true,
    forms: { interactive: true, xfa: true },
  },
  behavior: {
    zoomGestures: true,
    textSelectionPersistence: "sticky",
  },
});

PDF JavaScript is unsupported and never executed. Print, SaveAs, submission/network, rich-media, and scripting annotation actions are filtered. Read-only markup, internal/external links, supported forms, and optional-content layers remain independently configurable. See common options for feature behavior and security guidance for deployment boundaries.

Reuse And Clean Up

close() releases the current document while retaining the viewer and its controls for another load(). destroy() is terminal and idempotent; generated UI is removed, while caller-owned roots and custom markup are preserved. A viewer also destroys itself when its root disconnects.

await viewer.load("/documents/second.pdf");
await viewer.close();
await viewer.load(pdfBytes);
viewer.destroy();
runtime.destroy(); // Only after every attached viewer is destroyed.

One runtime normally serves every visible viewer in an application. The detailed guide covers shared workers, multi-viewer activation, global keyboard scope, hash-backed navigation, downloads, document queries, diagnostics, and controlled printing.

Project History

This viewer began as part of Unilaiva, my songbook site. I wanted readers to have a consistent experience across desktop and mobile browsers, inspired by desktop Firefox's PDF viewer, which I felt was the best fit for reading and navigating a songbook.

That use case shaped the priorities: an outline for finding songs, internal links, keyboard navigation, book-like two-page presentation when space allows, and comfortable scrolling and zoom gestures across devices. Over the years, the viewer grew beyond the original site, and I began to see it as something other projects could use too.

In 2026, I found LLM assistance useful enough to help prepare it for that broader role, including adding features the songbook had not needed, notably form support. That work led to the decision to separate the viewer into a reusable open-source package. Its focus remains the same: a comfortable reading experience, with the surrounding application free to make it its own.

Development

Build, test, and local demo commands are in the maintainer guide. Its architecture and release-qualification guidance is not required to integrate the published package.

License

Copyright © 2022-2026 Lari Natri. Licensed under Apache-2.0. PDF.js/pdfjs-dist is a separate peer dependency under its own license.