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

@cocoar/vue-document-viewer

v2.5.2

Published

Universal document viewer for Vue 3 — PDFs, images, and more, with annotations and Cocoar Design System styling

Downloads

527

Readme

@cocoar/vue-document-viewer

Universal document viewer for Vue 3 — renders PDFs, single images, and multi-page image galleries through a single component with shared toolbar, panels, and annotation layer.

Status: Preview tier. Ships under the suite version (GitVersion-calculated) but the public API is still settling — small tweaks may land before the Preview marker drops.

📖 Full reference, live demos, and configuration guide: docs.cocoar.dev/cocoar-ui-vue/components/document-viewer/

Install

pnpm add @cocoar/vue-document-viewer @cocoar/vue-ui

@cocoar/vue-ui is a peer dependency. pdfjs-dist is optional — only required if you render PDFs:

pnpm add pdfjs-dist     # PDF consumers only

Image-only consumers (imageSource, imageGallerySource) skip pdfjs entirely.

Three source factories

import {
  CoarDocumentViewer,
  imageSource,
  imageGallerySource,
} from '@cocoar/vue-document-viewer';
import { pdfSource } from '@cocoar/vue-document-viewer/pdf';
import '@cocoar/vue-document-viewer/styles';

pdfSource({ url, headers?, withCredentials? })   // PDF — pdfjs subpath
imageSource({ url })                              // Single image (JPG / PNG / SVG / WebP / AVIF / GIF / blob: / data:)
imageGallerySource({ urls })                      // Multi-page image document

Each factory returns a frozen DocumentSource. Build it inside a computed so the viewer rebinds only on real source changes.

Quick start — image

<script setup lang="ts">
import { computed } from 'vue';
import { CoarDocumentViewer, imageSource } from '@cocoar/vue-document-viewer';
import '@cocoar/vue-document-viewer/styles';

const source = computed(() => imageSource({ url: '/attachments/diagram.png' }));
</script>

<template>
  <div style="height: 80vh">
    <CoarDocumentViewer :source="source" />
  </div>
</template>

Quick start — image gallery

<script setup lang="ts">
import { computed } from 'vue';
import { CoarDocumentViewer, imageGallerySource } from '@cocoar/vue-document-viewer';

const source = computed(() => imageGallerySource({
  urls: ['/scans/page-1.jpg', '/scans/page-2.jpg', '/scans/page-3.jpg'],
}));
</script>

<template>
  <CoarDocumentViewer :source="source" :show-thumbnails="true" />
</template>

Pages may mix orientations — each carries its own intrinsic dimensions.

Quick start — PDF

PDFs need a one-line worker setup at app bootstrap (Vite shown — webpack, Rollup, esbuild all support ?worker):

// main.ts
import * as pdfjs from 'pdfjs-dist';
import PdfWorker from 'pdfjs-dist/build/pdf.worker.min.mjs?worker';

pdfjs.GlobalWorkerOptions.workerPort = new PdfWorker();

Then:

<script setup lang="ts">
import { computed } from 'vue';
import { CoarDocumentViewer } from '@cocoar/vue-document-viewer';
import { pdfSource } from '@cocoar/vue-document-viewer/pdf';

const source = computed(() => pdfSource({
  url: '/api/files/contract.pdf',
  withCredentials: true,
  headers: { 'X-Tenant': 'acme' },
}));
</script>

<template>
  <CoarDocumentViewer
    :source="source"
    :show-thumbnails="true"
    :show-outline="true"
    :show-annotations-panel="true"
    :show-print-download="true"
  />
</template>

Capability-driven toolbar

Every source advertises what it can do via capabilities flags. The toolbar disables unsupported tools rather than hiding them — switching sources never makes buttons jump around. Search and outline are PDF-only; image sources see them greyed out with a tooltip suffix.

interface DocumentSourceCapabilities {
  multiPage: boolean;   // Prev / Next / page input
  textLayer: boolean;   // Text selection, Ctrl+C copy
  search: boolean;      // Search button
  outline: boolean;     // Outline (TOC) sidebar tab
  print: boolean;       // Print button
}

Order-driven custom toolbar

The tools prop drives BOTH the visible set AND order:

import type { CoarDocumentViewerTool } from '@cocoar/vue-document-viewer';

const MINIMAL: CoarDocumentViewerTool[] = [
  'prev-page', 'page-input', 'next-page',
  'separator',
  'zoom-out', 'zoom-reset', 'zoom-in',
];
<CoarDocumentViewer :source="source" :tools="MINIMAL" />

Leading + trailing separators auto-trim; consecutive separators collapse. See the toolbar guide for the full layer model.

Annotations

Four types — marker / comment / ink / freetext — controlled-component pattern: the viewer emits events, the consumer owns persistence.

<CoarDocumentViewer
  :source="source"
  v-model:annotation-mode="mode"
  :annotations="annotations"
  :show-annotations-panel="true"
  @annotation:create="onCreate"
  @annotation:update="onUpdate"
  @annotation:delete="onDelete"
/>

Coordinates are page-relative and normalized to [0..1], so annotations render correctly across zoom levels and rotations and stay portable across rendering contexts. Full lifecycle, types, and a worked review example in the annotations guide.

Position memory

Two compatible mechanisms — pick one:

<!-- Automatic localStorage persistence -->
<CoarDocumentViewer :source="source" storage-key="contract-abc" />

<!-- Or two-way binding for your own state -->
<CoarDocumentViewer :source="source" v-model:position="position" />

Position covers { page, pageOffset, zoom, rotation }.

Theming

Component visuals expose CSS custom properties — override on any ancestor:

.my-doc-modal {
  --coar-pdf-toolbar-bg: #1f2937;
  --coar-pdf-toolbar-fg: #f9fafb;
  --coar-pdf-page-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
  --coar-pdf-highlight-color: rgba(250, 204, 21, 0.5);
}

(The --coar-pdf-* prefix is historical — the component was renamed from vue-pdf-viewer to vue-document-viewer but the CSS variable names stay stable for back-compat.)

Known limitations

  • PDF worker setup must be done by the consumer (shown above) — the package can't do it for you because Vite's ?worker import is a build-time concern.
  • Large PDFs (> 50 MB) are not specifically optimized yet — pages render lazily, but the document is loaded fully.
  • Gallery bulk-print prints the visible page only; multi-page print for galleries is on the roadmap.
  • Cross-origin auth with cookies on a different origin (CORS-with-credentials) is out of scope; same-origin cookies via withCredentials: true work.

Architecture

The viewer is source-agnostic internally — every source kind (PDF page proxy, HTMLImageElement, future kinds) materializes through the same PageProvider interface (render(canvas, opts), cancel(), optional getTextLayer()). The page renderer never imports pdfjs-dist.

The seam keeps the public component identical across formats: same toolbar, same side panels, same annotation surface. Adding a future source kind (OCR'd images, DOCX, etc.) is a matter of providing a new factory + adapter.

License

Apache 2.0. See LICENSE.