Skip to content

Repository files navigation

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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages