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

stimulus-pdf-viewer

v0.2.1

Published

PDF viewer with annotation support for Stimulus and Hotwire

Readme

stimulus-pdf-viewer

A full-featured PDF viewer with annotation support, built for Stimulus and the Hotwire ecosystem.

Features

  • PDF Rendering - Powered by Mozilla's PDF.js with lazy page loading
  • Annotations - Highlights, underlines, sticky notes, and freehand drawing
  • Search - Find text within the document with keyboard shortcuts
  • Thumbnails - Page thumbnail sidebar for quick navigation
  • Zoom Controls - Fit to page, fit to width, or custom zoom levels
  • Watermarks - User-specific watermarks for document security
  • Download - Export PDFs with annotations embedded
  • Mobile Support - Touch gestures, responsive toolbar, pinch-to-zoom
  • Accessibility - Screen reader support, keyboard navigation, high contrast mode

Installation

npm install stimulus-pdf-viewer @hotwired/stimulus @rails/request.js pdfjs-dist pdf-lib

Or with yarn:

yarn add stimulus-pdf-viewer @hotwired/stimulus @rails/request.js pdfjs-dist pdf-lib

Using Importmap (Rails 7+)

Option 1: Download vendored files (Recommended)

Download the packages to your vendor directory:

bin/importmap pin stimulus-pdf-viewer pdfjs-dist pdf-lib --download

Then download the PDF.js worker file separately:

curl -o vendor/javascript/pdfjs-dist--pdf.worker.js \
  "https://cdn.jsdelivr.net/npm/[email protected]/build/pdf.worker.mjs"

Add the worker to your config/importmap.rb:

pin "pdfjs-dist/build/pdf.worker.mjs", to: "pdfjs-dist--pdf.worker.js"

Option 2: Use CDN URLs

Add the following to your config/importmap.rb:

pin "stimulus-pdf-viewer", to: "https://ga.jspm.io/npm:[email protected]/dist/stimulus-pdf-viewer.esm.js"
pin "pdfjs-dist", to: "https://ga.jspm.io/npm:[email protected]/build/pdf.mjs"
pin "pdfjs-dist/build/pdf.worker.mjs", to: "https://ga.jspm.io/npm:[email protected]/build/pdf.worker.mjs"
pin "pdf-lib", to: "https://ga.jspm.io/npm:[email protected]/dist/pdf-lib.esm.js"

Quick Start

1. Register the controllers

import { Application } from "@hotwired/stimulus"
import { PdfViewerController, PdfDownloadController } from "stimulus-pdf-viewer"

const application = Application.start()
application.register("pdf-viewer", PdfViewerController)
application.register("pdf-download", PdfDownloadController)

2. Add the styles

// In your application.scss
@import "stimulus-pdf-viewer/styles/pdf-viewer";

Or copy styles/pdf-viewer.scss to your stylesheets directory.

3. Copy cursor assets

Copy the cursor SVG files from assets/cursors/ to your asset pipeline (e.g., app/assets/images/pdf_viewer/).

4. Set up the PDF.js worker

Add a meta tag to configure the PDF.js worker path:

<meta name="pdf-worker-src" content="<%= asset_path('pdfjs-dist--pdf.worker.js') %>">

5. Create the viewer HTML

<div data-controller="pdf-viewer"
     data-pdf-viewer-document-url-value="<%= url_for(@document.file) %>"
     data-pdf-viewer-document-name-value="<%= @document.name %>"
     data-pdf-viewer-annotations-url-value="<%= document_annotations_path(@document) %>"
     data-pdf-viewer-user-name-value="<%= current_user.name %>"
     class="pdf-viewer-container">

  <!-- Toolbar -->
  <div class="pdf-toolbar">
    <!-- See examples/rails/_toolbar.html.erb for full toolbar markup -->
  </div>

  <!-- Viewer body -->
  <div class="pdf-viewer-body">
    <div class="pdf-pages-container" data-pdf-viewer-target="container"></div>
  </div>

  <!-- Loading overlay -->
  <div class="pdf-loading-overlay" data-pdf-viewer-target="loadingOverlay">
    <div class="pdf-loading-spinner"></div>
    <div class="pdf-loading-text">Loading document...</div>
  </div>
</div>

Configuration

The PdfViewerController accepts the following Stimulus values:

| Value | Type | Description | |-------|------|-------------| | documentUrl | String | URL to the PDF file | | documentName | String | Display name for downloads | | annotationsUrl | String | REST API endpoint for annotations | | trackingUrl | String | (Optional) Endpoint for time tracking | | userName | String | User name for watermarks | | organizationName | String | Organization name for watermarks | | initialPage | Number | Page to open on load (default: 1) | | initialAnnotation | String | Annotation ID to highlight on load |

Annotations API

The viewer expects a REST API at annotationsUrl with these endpoints:

| Method | Path | Description | |--------|------|-------------| | GET | {annotationsUrl}.json | List all annotations | | POST | {annotationsUrl} | Create annotation | | PATCH | {annotationsUrl}/{id} | Update annotation | | DELETE | {annotationsUrl}/{id} | Delete annotation | | PATCH | {annotationsUrl}/{id}/restore | Restore deleted annotation |

Annotation JSON Schema

{
  "id": "uuid",
  "page": 1,
  "annotation_type": "highlight|underline|note|ink",
  "color": "#FFEB3B",
  "opacity": 0.4,
  "quads": [{"p1": {"x": 100, "y": 200}, "p2": {...}, "p3": {...}, "p4": {...}}],
  "rect": [100, 200, 124, 224],
  "contents": "Note text content",
  "ink_strokes": [{"points": [{"x": 100, "y": 200}]}],
  "thickness": 2
}

Events

The viewer dispatches these custom events on the container element:

| Event | Description | |-------|-------------| | pdf-viewer:ready | Document loaded and ready | | pdf-viewer:page-changed | User navigated to a different page | | pdf-viewer:annotation-created | New annotation created | | pdf-viewer:annotation-updated | Annotation modified | | pdf-viewer:annotation-deleted | Annotation deleted | | pdf-viewer:annotation-selected | User selected an annotation | | pdf-viewer:scale-changed | Zoom level changed | | pdf-viewer:error | Error occurred |

Error Handling

Pass an onError callback when using the PdfViewer class directly:

const viewer = new PdfViewer(container, {
  documentUrl: "/path/to/document.pdf",
  onError: (error) => {
    // Send to your error tracking service
    Sentry.captureException(error)
  }
})

Rails Integration

For the easiest Rails setup, use the stimulus-pdf-viewer-rails gem which handles asset configuration automatically.

For manual integration, see examples/rails/ for complete Rails integration examples including:

  • View partials for the viewer and toolbar
  • Annotations controller
  • Annotation model with validations

Browser Support

  • Chrome/Edge 88+
  • Firefox 78+
  • Safari 14+
  • Mobile Safari (iOS 14+)
  • Chrome for Android

License

This project is dual-licensed:

See the NOTICE file for attribution details.

Credits

Built with:

  • PDF.js - PDF rendering engine (Apache 2.0). Portions of this library's rendering queue, text layer selection, and search functionality are derived from PDF.js patterns.
  • pdf-lib - PDF manipulation for downloads
  • Stimulus - JavaScript framework
  • @rails/request.js - HTTP requests with Turbo Stream support