pdf-virtual-viewer
v0.1.3
Published
A minimal virtualized PDF viewer that lazy-loads pages via HTTP range requests.
Maintainers
Readme
pdf-virtual-viewer
A minimal virtualized PDF viewer for the browser. Pages are fetched with HTTP range requests and only nearby pages are rendered, so large documents stay light.
Built on pdf.js (pdfjs-dist).
Install
npm i pdf-virtual-viewer pdfjs-distpdfjs-dist is a peer (and runtime) dependency. Import the stylesheet once:
import "pdf-virtual-viewer/styles.css";Quick start
The container must have a definite height and display: flex (the scroller fills it).
<div id="viewer" style="height: 80vh; display: flex; overflow: hidden"></div>import { PdfVirtualViewer } from "pdf-virtual-viewer";
import "pdf-virtual-viewer/styles.css";
const viewer = new PdfVirtualViewer(document.getElementById("viewer")!, {
onStatus: (text) => console.log(text),
});
await viewer.load("https://example.com/book.pdf");Worker
If pdfjs-dist has no GlobalWorkerOptions.workerSrc yet, the library points at the cdnjs build of pdf.js 4.10.38. To use a local worker instead:
import { GlobalWorkerOptions } from "pdfjs-dist";
GlobalWorkerOptions.workerSrc = new URL(
"pdfjs-dist/build/pdf.worker.min.mjs",
import.meta.url,
).toString();Set this before constructing PdfVirtualViewer.
CORS and range requests
The viewer opens the PDF with disableAutoFetch and disableStream, so the host must:
- Allow cross-origin reads from your page (
Access-Control-Allow-Origin). - Support HTTP range requests (
Accept-Ranges: bytes) and exposeContent-Range/Accept-Rangesto the browser (Access-Control-Expose-Headers).
Same-origin files (or a same-origin proxy) skip the CORS requirement. The included demo server (npm run serve) proxies remote URLs at /proxy?url= for local testing.
API
import { PdfVirtualViewer } from "pdf-virtual-viewer";
import type { PdfVirtualViewerOptions, Annotations } from "pdf-virtual-viewer";
const viewer = new PdfVirtualViewer(container, options?);
await viewer.load(url);
viewer.setAnnotation(page, text);
viewer.removeAnnotation(page);
viewer.getAnnotations(); // Record<number, string>
viewer.setAnnotations(map);
viewer.destroy();PdfVirtualViewerOptions
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| onStatus | (status: string) => void | — | Load / render progress text |
| prefetchPages | number | 2 | Extra pages to render past the viewport |
| renderMargin | number | 800 | Extra pixels around the viewport that count as “near” |
| maxPageWidth | number | 900 | Cap on rendered page width (px) |
| annotations | Record<number, string> | {} | Initial per-page notes (1-based page numbers) |
| onAnnotationsChange | (annotations) => void | — | Fired after add / edit / delete |
Page numbers in annotations are 1-based.
Styles
import "pdf-virtual-viewer/styles.css";The stylesheet is also exported as pdf-virtual-viewer/styles.css (file: src/pdf-viewer.css). Layout classes:
.pvv-scroller— scrollport (needs a flex parent with a height).pvv-pages/.pvv-page-slot/.pvv-page— virtualized page stack.pvv-annotate-strip— note row under each page
Demo
git clone https://github.com/thejsmaster/pdf-virtual-viewer
cd pdf-virtual-viewer
npm i
npm run serveOpens on http://localhost:8080. Paste a PDF URL and press Load. Remote files go through /proxy?url= so range requests work locally.
npm run make-sample -- 40 # optional: generate a multi-page sample PDFHow it works
pdfjs-distloads the document with range requests (disableAutoFetch/disableStream).- Empty page slots are laid out for the full page count.
- On scroll / resize, only slots near the viewport (plus
prefetchPages) are rendered to canvas. - Each page has an optional annotation strip underneath.
License
MIT
