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

@emsun/react-doc-viewer

v1.0.1

Published

Headless React document viewer supporting PDF, DOCX, Markdown, code, images, and video

Downloads

310

Readme

@emsun/react-doc-viewer

A headless React document viewer. You supply all layout and styles via render props — the library handles loading and rendering documents only.

Supports: PDF, DOCX, Markdown, plain text, code, images, and video (including direct HTTPS URLs and YouTube/Vimeo embeds).


Installation

npm install @emsun/react-doc-viewer

Peer dependencies:

npm install react react-dom

Quick start

import { DocumentViewer } from "@emsun/react-doc-viewer";

function App() {
  return (
    <DocumentViewer
      source={{
        uri: "https://example.com/report.pdf",
        name: "Q4 Report",
      }}
      className="my-viewer"
      renderLoading={({ source }) => (
        <p>Loading {source.name ?? "document"}…</p>
      )}
      renderError={({ error, source }) => (
        <p>Failed to load {source.name}: {error}</p>
      )}
      renderToolbar={({ state, prevPage, nextPage, zoomIn, zoomOut, setZoom }) => (
        <div className="toolbar">
          {state.docType === "pdf" && state.totalPages > 0 && (
            <>
              <button onClick={prevPage} disabled={state.currentPage <= 1}>←</button>
              <span>{state.currentPage} / {state.totalPages}</span>
              <button onClick={nextPage} disabled={state.currentPage >= state.totalPages}>→</button>
            </>
          )}
          <button onClick={zoomOut}>−</button>
          <span>{Math.round(state.zoom * 100)}%</span>
          <button onClick={zoomIn}>+</button>
          <button onClick={() => setZoom(1)}>100%</button>
        </div>
      )}
    />
  );
}

Remote URLs (HTTPS)

Pass any reachable URL as source.uri:

<DocumentViewer source={{ uri: "https://cdn.example.com/files/report.pdf" }} />
<DocumentViewer source={{ uri: "https://cdn.example.com/photo.png" }} />
<DocumentViewer source={{ uri: "https://cdn.example.com/clip.mp4", mimeType: "video/mp4" }} />

| Format | HTTPS support | Notes | |--------|---------------|--------| | PDF | Yes | Fetched via pdf.js — host must allow CORS | | DOCX, Markdown, text, code | Yes | Uses fetch()CORS required | | Images | Yes | <img src="https://…"> | | Video (.mp4, .webm, …) | Yes | Direct file URL in <video> | | YouTube / Vimeo | Yes | Page URL auto-embedded via <iframe> |

If a remote PDF or DOCX loads locally but fails from a URL, configure Access-Control-Allow-Origin on the file host or proxy the file through your API.

YouTube & Vimeo

Use the watch/share page URL (not a download link):

<DocumentViewer
  source={{
    uri: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    name: "Demo video",
  }}
/>

<DocumentViewer
  source={{
    uri: "https://vimeo.com/123456789",
    name: "Vimeo demo",
  }}
/>

Supported YouTube patterns: youtube.com/watch?v=, youtu.be/, /embed/, /shorts/.

Custom container

Use renderContainer when you need full control over the outer layout:

<DocumentViewer
  source={source}
  renderContainer={({ children }) => (
    <div className="viewer-shell">{children}</div>
  )}
  renderToolbar={(props) => <MyToolbar {...props} />}
/>

When using renderContainer, className on DocumentViewer is not applied — put layout classes on your wrapper instead.


Headless hook

Use useDocViewer when you build the UI yourself:

import {
  useDocViewer,
  PdfRenderer,
  DocxRenderer,
  detectDocType,
} from "@emsun/react-doc-viewer";

function MyViewer({ uri }: { uri: string }) {
  const source = { uri };
  const viewer = useDocViewer({
    source,
    onLoad: (s) => console.log("Ready", s.docType, s.totalPages),
    onError: (err) => console.error(err),
  });

  const { state, dispatch, nextPage, prevPage, zoomIn, zoomOut } = viewer;
  const docType = state.docType !== "unknown" ? state.docType : detectDocType(source);

  return (
    <div className="layout">
      <aside>
        <button onClick={prevPage}>Prev</button>
        <button onClick={nextPage}>Next</button>
        <button onClick={zoomIn}>Zoom in</button>
        <button onClick={zoomOut}>Zoom out</button>
      </aside>
      <main>
        {state.status === "loading" && <p>Loading…</p>}
        {state.status === "error" && <p>{state.error}</p>}
        {docType === "pdf" && (
          <PdfRenderer source={source} state={state} dispatch={dispatch} />
        )}
        {docType === "docx" && (
          <DocxRenderer source={source} state={state} dispatch={dispatch} />
        )}
      </main>
    </div>
  );
}

Supported document types

| Type | Extensions / detection | Renderer | Runtime dependency | |------|------------------------|----------|-------------------| | PDF | .pdf | PdfRenderer | pdf.js 4.x (CDN) | | Word | .docx, .doc | DocxRenderer | mammoth.js 1.6 (CDN) | | Markdown | .md, .mdx | MarkdownRenderer | marked 12.x (CDN) | | Text | .txt, .log | TextRenderer | — | | Code | .js, .ts, .py, .json, … | TextRenderer | — | | Image | .png, .jpg, .gif, .webp, .svg, … | ImageRenderer | — | | Video | .mp4, .webm, .mov, YouTube, Vimeo | VideoRenderer | — |

Type is auto-detected from the file extension, mimeType, or URL shape (e.g. YouTube). Override with source.type.

interface DocSource {
  uri: string;        // HTTPS URL, path, or data: URI
  type?: DocType;     // Override auto-detection
  name?: string;      // Display name (toolbar, errors)
  mimeType?: string;  // Stricter MIME detection
}

API reference

DocumentViewer props

| Prop | Type | Description | |------|------|-------------| | source | DocSource | Required. Document to display | | renderContainer | (props & { children }) => ReactNode | Replace the root wrapper | | renderLoading | ({ source }) => ReactNode | Shown while status === "loading" | | renderError | ({ error, source }) => ReactNode | Shown when status === "error" | | renderToolbar | ViewerRenderProps => ReactNode | Your toolbar (navigation, zoom, etc.) | | renderCanvasToolbar | ViewerRenderProps => ReactNode | Shown when state.canvasMode is true (advanced) | | onLoad | (state) => void | Document ready | | onError | (error: string) => void | Load failed | | onAnnotationsChange | (annotations) => void | Annotation state changed (advanced) | | initialAnnotations | CanvasAnnotation[] | Pre-load annotations (advanced) | | className | string | On default root only (ignored if renderContainer is used) | | style | CSSProperties | On default root only |

ViewerRenderProps

Passed to renderToolbar, renderCanvasToolbar, and renderContainer:

| Property / method | Description | |-------------------|-------------| | state | Full ViewerState (status, docType, currentPage, totalPages, zoom, …) | | goToPage, nextPage, prevPage | PDF page navigation | | setZoom, zoomIn, zoomOut | Zoom (PDF and other renderers that respect state.zoom) | | toggleCanvasMode | Toggle drawing mode flag (advanced) | | setDrawingTool, setDrawingColor, setLineWidth | Drawing controls (advanced) | | clearAnnotations, deleteAnnotation | Annotation management (advanced) | | exportAnnotations, loadAnnotations | Serialize / restore annotations (advanced) |

Public exports

// Component & hook
DocumentViewer
useDocViewer

// Renderers (compose your own viewer)
PdfRenderer, DocxRenderer, MarkdownRenderer, TextRenderer
ImageRenderer, VideoRenderer

// Advanced
DrawingCanvas
viewerReducer, initialViewerState, initialCanvasState
detectDocType, generateId, getRelativePoint

Styling with data-rdv-* attributes

Renderers expose stable data-rdv-* hooks for CSS — no bundled styles:

[data-rdv-root] { /* default wrapper */ }
[data-rdv-toolbar] { /* your toolbar if you set data-rdv-toolbar */ }
[data-rdv-pdf-container] { /* PDF page area */ }
[data-rdv-pdf-canvas] { /* pdf.js canvas */ }
[data-rdv-docx-content] { /* converted DOCX HTML */ }
[data-rdv-markdown-content] { /* rendered markdown */ }
[data-rdv-text-content] { /* plain text / code */ }
[data-rdv-image] { /* image */ }
[data-rdv-video] { /* HTML5 video */ }
[data-rdv-video-iframe] { /* YouTube / Vimeo embed */ }

PDF drawing (advanced)

The package exports DrawingCanvas, annotation types, and hook methods for a future drawing layer. v1 PdfRenderer does not mount the drawing overlay — PDFs render as pages only. To experiment, compose DrawingCanvas yourself next to PdfRenderer using useDocViewer state.


Development

Clone the repo and run the local playground:

npm install
npm run dev      # http://localhost:5173 — sample docs in sidebar
npm run build    # library output in dist/
npm run type-check

Build artifacts:

  • dist/index.js — ESM
  • dist/index.cjs — CommonJS
  • dist/index.d.ts — TypeScript declarations

License

MIT © Gbenga Akinnukawe