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 🙏

© 2025 – Pkg Stats / Ryan Hefner

js-doc-viewer-universal

v1.0.0

Published

A framework-agnostic document viewer library for JavaScript and TypeScript supporting PDF, Office documents, images, and text files

Readme

Universal Document Viewer

A framework-agnostic document viewer library for JavaScript and TypeScript that allows you to display various document formats in your web applications.

Features

  • Framework Independent: Works with vanilla JavaScript, React, Vue, Angular, Svelte, and any other framework
  • TypeScript Support: Fully typed for better developer experience
  • Multiple Formats: Supports PDF, Office documents (XLS/XLSX, DOC/DOCX, PPT/PPTX), images (PNG, JPG/JPEG), and text files
  • Easy to Use: Simple API with minimal configuration
  • Customizable: Flexible styling and configuration options
  • Lightweight: Minimal dependencies

Supported Formats

  • PDF (.pdf)
  • Excel (.xls, .xlsx)
  • Word (.doc, .docx)
  • PowerPoint (.ppt, .pptx)
  • Images (.png, .jpg, .jpeg)
  • Text (.txt)

Installation

npm install universal-doc-viewer

Or with yarn:

yarn add universal-doc-viewer

Development / Local Testing

If you're working with the source code:

# Clone the repository
git clone <repository-url>
cd ng-doc-viewer

# Install dependencies
npm install

# Build the library (REQUIRED before testing)
npm run build

# Test with a local HTTP server
python3 -m http.server 8000
# Then open http://localhost:8000/simple-test.html

See TESTING.md for detailed testing instructions.

Quick Start

Vanilla JavaScript

import { DocumentViewer } from 'universal-doc-viewer';

const viewer = new DocumentViewer({
  container: '#viewer-container',
  url: 'https://pdfobject.com/pdf/sample.pdf',
  width: '100%',
  height: '800px'
});

viewer.render();

Using the Helper Function

import { createViewer } from 'universal-doc-viewer';

const viewer = await createViewer({
  container: '#viewer-container',
  url: 'https://pdfobject.com/pdf/sample.pdf'
});

HTML

<!DOCTYPE html>
<html>
<head>
  <title>Document Viewer</title>
</head>
<body>
  <div id="viewer-container"></div>

  <script type="module">
    import { DocumentViewer } from 'universal-doc-viewer';

    const viewer = new DocumentViewer({
      container: '#viewer-container',
      url: 'https://pdfobject.com/pdf/sample.pdf',
      height: '90vh'
    });

    viewer.render();
  </script>
</body>
</html>

Framework Integration

React

import { useEffect, useRef } from 'react';
import { DocumentViewer } from 'universal-doc-viewer';

function DocumentViewerComponent({ url }) {
  const containerRef = useRef(null);
  const viewerRef = useRef(null);

  useEffect(() => {
    if (containerRef.current) {
      viewerRef.current = new DocumentViewer({
        container: containerRef.current,
        url: url,
        height: '600px'
      });

      viewerRef.current.render();
    }

    return () => {
      if (viewerRef.current) {
        viewerRef.current.destroy();
      }
    };
  }, [url]);

  return <div ref={containerRef}></div>;
}

Vue 3

<template>
  <div ref="viewerContainer"></div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, watch } from 'vue';
import { DocumentViewer } from 'universal-doc-viewer';

const props = defineProps({
  url: String
});

const viewerContainer = ref(null);
let viewer = null;

onMounted(() => {
  viewer = new DocumentViewer({
    container: viewerContainer.value,
    url: props.url,
    height: '600px'
  });

  viewer.render();
});

onUnmounted(() => {
  if (viewer) {
    viewer.destroy();
  }
});

watch(() => props.url, (newUrl) => {
  if (viewer) {
    viewer.changeDocument(newUrl);
  }
});
</script>

Angular

import { Component, ElementRef, Input, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { DocumentViewer } from 'universal-doc-viewer';

@Component({
  selector: 'app-document-viewer',
  template: '<div #viewerContainer></div>'
})
export class DocumentViewerComponent implements OnInit, OnDestroy {
  @Input() url: string;
  @ViewChild('viewerContainer', { static: true }) viewerContainer: ElementRef;

  private viewer: DocumentViewer;

  ngOnInit() {
    this.viewer = new DocumentViewer({
      container: this.viewerContainer.nativeElement,
      url: this.url,
      height: '600px'
    });

    this.viewer.render();
  }

  ngOnDestroy() {
    if (this.viewer) {
      this.viewer.destroy();
    }
  }
}

Svelte

<script>
  import { onMount, onDestroy } from 'svelte';
  import { DocumentViewer } from 'universal-doc-viewer';

  export let url;

  let viewerContainer;
  let viewer;

  onMount(() => {
    viewer = new DocumentViewer({
      container: viewerContainer,
      url: url,
      height: '600px'
    });

    viewer.render();
  });

  onDestroy(() => {
    if (viewer) {
      viewer.destroy();
    }
  });

  $: if (viewer && url) {
    viewer.changeDocument(url);
  }
</script>

<div bind:this={viewerContainer}></div>

API Reference

DocumentViewer

The main class for creating document viewers.

Constructor

new DocumentViewer(options: ViewerOptions)

Options

interface ViewerOptions {
  // Required
  container: HTMLElement | string;  // Container element or selector
  url: string;                       // Document URL

  // Optional
  type?: DocumentType;               // Document type (auto-detected if not provided)
  width?: string;                    // Viewer width (default: '100%')
  height?: string;                   // Viewer height (default: '600px')
  styles?: Partial<CSSStyleDeclaration>; // Custom styles
  showLoader?: boolean;              // Show loading indicator (default: true)
  loadingMessage?: string;           // Custom loading message
  useGoogleDocsViewer?: boolean;     // Use Google Docs for Office files (default: true)
  useMicrosoftViewer?: boolean;      // Use Microsoft viewer for Office files (default: false)
  useBlobFallback?: boolean;         // Use blob/memory fallback for CORS issues (default: true)
  onLoad?: () => void;               // Success callback
  onError?: (error: Error) => void;  // Error callback
}

Methods

render()

Renders the document in the container.

await viewer.render();
reload()

Reloads the current document.

await viewer.reload();
destroy()

Destroys the viewer and cleans up resources.

viewer.destroy();
changeDocument(url, type?)

Changes the document and re-renders.

await viewer.changeDocument('https://example.com/new-document.pdf');
getDocumentType()

Gets the current document type.

const type = viewer.getDocumentType(); // 'pdf' | 'xlsx' | etc.
isPDF(), isImage(), isOfficeDocument()

Helper methods to check document type.

if (viewer.isPDF()) {
  console.log('Viewing a PDF');
}

Advanced Usage

Custom Styling

const viewer = new DocumentViewer({
  container: '#viewer',
  url: 'document.pdf',
  styles: {
    border: '2px solid #333',
    borderRadius: '8px',
    boxShadow: '0 4px 6px rgba(0,0,0,0.1)'
  }
});

Error Handling

const viewer = new DocumentViewer({
  container: '#viewer',
  url: 'document.pdf',
  onError: (error) => {
    console.error('Failed to load document:', error);
    alert('Could not load the document. Please try again.');
  },
  onLoad: () => {
    console.log('Document loaded successfully!');
  }
});

Office Document Viewer Selection

// Use Microsoft Office Online Viewer
const viewer = new DocumentViewer({
  container: '#viewer',
  url: 'spreadsheet.xlsx',
  useMicrosoftViewer: true
});

// Use Google Docs Viewer (default)
const viewer2 = new DocumentViewer({
  container: '#viewer2',
  url: 'presentation.pptx',
  useGoogleDocsViewer: true
});

Dynamic Document Switching

const viewer = new DocumentViewer({
  container: '#viewer',
  url: 'initial-document.pdf',
  height: '700px'
});

await viewer.render();

// Later, change to a different document
document.getElementById('changeBtn').addEventListener('click', async () => {
  await viewer.changeDocument('new-document.docx');
});

Using Individual Viewers

You can also use individual viewer classes directly:

import { PDFViewer, ImageViewer, TextViewer, OfficeViewer } from 'universal-doc-viewer';

// PDF Viewer
const pdfViewer = new PDFViewer(containerElement, {
  container: containerElement,
  url: 'document.pdf'
});
await pdfViewer.render();

// Image Viewer
const imageViewer = new ImageViewer(containerElement, {
  container: containerElement,
  url: 'image.png'
});
await imageViewer.render();

Browser Compatibility

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)

Important Notes

Office Documents

Office documents (Excel, Word, PowerPoint) are displayed using online viewers:

  • Google Docs Viewer (default): Free, no authentication required
  • Microsoft Office Online Viewer: Alternative option

Note: Both viewers require the document to be publicly accessible via a URL. Local files or authentication-protected files may not work with these viewers.

PDF Files

PDF files are rendered using PDF.js, which provides:

  • Client-side rendering
  • Page navigation controls
  • High-quality rendering

Loading Strategy: The library automatically loads PDF.js in one of two ways:

  1. Bundled environments (Webpack, Rollup, etc.): If you're using a bundler, you can optionally install pdfjs-dist as a dependency:

    npm install pdfjs-dist

    The library will use the bundled version automatically.

  2. Browser/CDN usage: If pdfjs-dist is not installed or the import fails, the library automatically falls back to loading PDF.js from CDN (cdnjs.cloudflare.com). No additional configuration needed!

This dual approach ensures the library works seamlessly in any environment, whether you're using it directly in the browser or with a build tool.

CORS Considerations and Blob Fallback

When loading documents from external URLs, CORS (Cross-Origin Resource Sharing) restrictions may prevent direct loading. To handle this, the library includes an automatic blob/memory fallback feature.

How Blob Fallback Works

When direct URL loading fails (typically due to CORS errors), the library automatically:

  1. Downloads the file to browser memory using fetch()
  2. Creates a blob URL from the downloaded data
  3. Uses the blob URL to render the document

This feature is enabled by default for PDFs, images, and text files. It works automatically without any configuration.

Controlling Blob Fallback

// Default behavior - blob fallback is automatic on CORS errors
const viewer = new DocumentViewer({
  container: '#viewer',
  url: 'https://example.com/document.pdf'
});

// Explicitly enable blob fallback (force download to memory)
const viewer2 = new DocumentViewer({
  container: '#viewer',
  url: 'https://example.com/document.pdf',
  useBlobFallback: true  // Always use blob fallback
});

// Disable blob fallback (fail if direct loading doesn't work)
const viewer3 = new DocumentViewer({
  container: '#viewer',
  url: 'https://example.com/document.pdf',
  useBlobFallback: false  // Never use blob fallback
});

Testing Blob Fallback

Open test-blob-fallback.html in your browser to test the blob fallback functionality:

python3 -m http.server 8000
# Open http://localhost:8000/test-blob-fallback.html

The test page includes buttons to:

  • Load documents normally (with automatic fallback)
  • Force blob fallback mode
  • View detailed console logs of the fallback process

Important Notes

  • Office Documents: Blob fallback does NOT work for Office documents (Excel, Word, PowerPoint) because they use third-party viewers (Google Docs Viewer or Microsoft Office Online) which require the URL to be publicly accessible from their servers.
  • Memory Usage: When using blob fallback, the entire document is loaded into browser memory. For large files, this may consume significant memory.
  • Browser Support: Blob fallback uses standard browser APIs (fetch, URL.createObjectURL) and works in all modern browsers.

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues and questions, please open an issue on the GitHub repository.