@relevaince/document-viewer
v0.3.2
Published
Citation-driven document viewer for Relevaince
Downloads
592
Maintainers
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-viewerReact 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 devOpens 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 + DOMPurifyDesign decisions:
- Peer deps for React — the host app controls the React version (18+).
- Code-split chunks —
tsupbuilds withsplitting: true. Browser-only renderers (PDF, Image, EML) are in separate chunks loaded viaReact.lazy(). The main entry has zero imports ofpdfjs-dist,react-pdf-highlighter-extended,react-zoom-pan-pinch, ordompurify, making it fully SSR-safe. - ClientOnly boundary — a
useState+useEffectguard 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@latestThe 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:
- You have
transpilePackagesset innext.config.js(see Usage with Next.js) - You're using the component inside a
"use client"boundary - You're on
@relevaince/document-viewerv0.3.0 or later
Building from source
npm install
npm run build # ESM + CJS + type declarations in dist/
npm run dev # watch modePublishing
npm version patch # or minor / major
npm run build
npm publishLicense
MIT
