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

@relevaince/document-viewer

v0.3.2

Published

Citation-driven document viewer for Relevaince

Downloads

592

Readme

@relevaince/document-viewer

A single React component to render PDF, DOCX, Image, JSON, and EML files. Built for citation-driven interfaces where users click a reference and the document opens, scrolled to the right page with the cited region highlighted.

Install

npm install @relevaince/document-viewer

React 18+ is required as a peer dependency.

Quick start

import { DocumentViewer } from "@relevaince/document-viewer";

function App() {
  return (
    <div style={{ width: "100%", height: "80vh" }}>
      <DocumentViewer
        target={{
          url: "https://example.com/report.pdf",
          page: 3,
          bbox: { x1: 0.1, y1: 0.2, x2: 0.5, y2: 0.3 },
        }}
      />
    </div>
  );
}

Pass target={null} to unmount the viewer. Change target to navigate between documents or citations.

Supported formats

| Format | Extensions | Renderer | | ------ | ---------- | -------- | | PDF | .pdf | react-pdf-highlighter-extended with bounding-box highlights | | DOCX | .doc, .docx | Microsoft Office Online embed (free, no server needed) | | Image | .jpg, .jpeg, .png, .gif, .webp, .bmp, .avif, .svg | react-zoom-pan-pinch with pan, zoom and pinch | | JSON | .json | Syntax-highlighted collapsible tree (zero-dep, built-in) | | E-mail | .eml | MIME parser with header extraction and HTML body sanitised via DOMPurify |

File type is auto-detected from the URL extension. Override it with the fileType prop when the URL doesn't contain an extension (e.g. pre-signed S3 URLs):

<DocumentViewer target={{ url: presignedUrl, fileType: "pdf" }} />

API

<DocumentViewer />

| Prop | Type | Description | | ---- | ---- | ----------- | | target | ViewerTarget \| null | The document to display. null hides the viewer. | | pdfWorkerUrl | string | Override the default pdfjs web-worker CDN URL. | | loadingComponent | ReactNode | Custom loading indicator. | | errorComponent | ReactNode | Custom error state. | | onLoadStart | () => void | Fires when loading begins. | | onLoadSuccess | () => void | Fires when the document finishes loading. | | onLoadError | (err: Error) => void | Fires on load failure. | | className | string | Class name for the outermost wrapper. |

ViewerTarget

type ViewerTarget = {
  url: string;
  fileType?: "pdf" | "docx" | "image" | "json" | "eml";
  page?: number;        // 1-indexed, PDF only
  bbox?: BBox;          // normalised 0–1 coords, PDF only
  text?: string;        // cited text snippet (future use)
  title?: string;       // display title
};

BBox

Bounding box in normalised coordinates (0–1), origin at the top-left of the page.

type BBox = { x1: number; y1: number; x2: number; y2: number };

getFileType(url)

Utility to infer file type from a URL. Returns "pdf" | "docx" | "image" | "json" | "eml" | "unknown".

import { getFileType } from "@relevaince/document-viewer";

getFileType("https://example.com/report.pdf"); // "pdf"
getFileType("https://example.com/photo.png");  // "image"

Usage with Next.js

The library is SSR-safe out of the box. PDF and Image renderers are lazy-loaded and wrapped in a ClientOnly boundary so pdfjs-dist and react-zoom-pan-pinch never execute on the server.

Add the package to transpilePackages in your next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
  transpilePackages: [
    "@relevaince/document-viewer",
    "react-pdf-highlighter-extended",
    "pdfjs-dist",
  ],
};

module.exports = nextConfig;

Then use it in any client component:

"use client";

import { DocumentViewer } from "@relevaince/document-viewer";

export function CitationPanel({ citation }) {
  return (
    <div style={{ width: "100%", height: "80vh" }}>
      <DocumentViewer
        target={{
          url: citation.documentUrl,
          page: citation.page,
          bbox: citation.bbox,
          title: citation.title,
        }}
      />
    </div>
  );
}

Running the demo

cd demo
npm install
npm run dev

Opens at localhost:3333. The demo shows a sidebar with PDF, Image, and JSON samples you can switch between.

Architecture

src/
  index.ts                          # Public exports
  types/
    ViewerTarget.ts                 # BBox, ViewerTarget, ViewerFileType
    DocumentViewerProps.ts          # Component props
  utils/
    getFileType.ts                  # URL → file type detection
  components/
    DocumentViewer.tsx              # Router + SSR-safe lazy loading
    renderers/
      PdfRenderer.tsx               # react-pdf-highlighter-extended
      DocxRenderer.tsx              # Office Online iframe
      ImageRenderer.tsx             # react-zoom-pan-pinch
      JsonRenderer.tsx              # Collapsible syntax tree
      EmlRenderer.tsx               # MIME parser + DOMPurify

Design decisions:

  • Peer deps for React — the host app controls the React version (18+).
  • Code-split chunkstsup builds with splitting: true. Browser-only renderers (PDF, Image, EML) are in separate chunks loaded via React.lazy(). The main entry has zero imports of pdfjs-dist, react-pdf-highlighter-extended, react-zoom-pan-pinch, or dompurify, making it fully SSR-safe.
  • ClientOnly boundary — a useState + useEffect guard ensures lazy chunks are never loaded during server rendering.
  • Normalised BBox — coordinates are 0–1 with origin at top-left, making them resolution-independent.
  • Zero-config — auto-detects file type, defaults the pdfjs worker to a CDN, no CSS imports required.

Troubleshooting

ReferenceError: window is not defined (Next.js SSR)

This was fixed in v0.3.0. If you're on an older version, upgrade:

npm install @relevaince/document-viewer@latest

The library uses code-split chunks so that pdfjs-dist and other browser-only dependencies are never loaded during server-side rendering. If you still see this error, make sure:

  1. You have transpilePackages set in next.config.js (see Usage with Next.js)
  2. You're using the component inside a "use client" boundary
  3. You're on @relevaince/document-viewer v0.3.0 or later

Building from source

npm install
npm run build        # ESM + CJS + type declarations in dist/
npm run dev          # watch mode

Publishing

npm version patch    # or minor / major
npm run build
npm publish

License

MIT