Skip to content

shubham8550/microscope-js

Repository files navigation

🔬 microscope-js

The file viewer for paranoid frontends.

Render PDF · Word · Excel · PowerPoint · image · video · audio — entirely in the browser. No Microsoft Online Viewer iframe. No Google Docs preview. No "we upload your file to our server first." The bytes never leave the tab.

CI Release Pages CodeQL

npm Downloads Bundle Tree-shakable Types Provenance Trusted Publisher

License Stars Forks Last commit Issues PRs welcome

TypeScript pnpm Changesets Biome Vitest

🌐 Live demo · 📚 API docs · 🐛 Issues · 💬 Discussions


✨ Why people pick microscope-js

  • 🔒 Zero data exfiltration. Documents are parsed in the user's browser. No backend round-trip, no third-party iframe, no telemetry.
  • 🪶 Pay for what you use. Every format lives in its own npm package; tree-shake or dynamic-import only what your app actually needs.
  • ⚛️ React & Next.js native. 'use client', drop in <Viewer />, done. The hook works with Suspense and the App Router.
  • 🧩 Pluggable. A renderer is 30 lines of TypeScript implementing one interface. Add a new format in an afternoon.
  • 🛡️ Hardened by default. DOMPurify-sanitized HTML, zip-slip & zip-bomb defenses, size caps, no eval, no dangerouslySetInnerHTML of user content.
  • 🔏 Signed releases. Every npm version ships with SLSA provenance via GitHub OIDC trusted publishing — verifiable build origin, no tokens involved.

🎬 Try it now

👉 shubham8550.github.io/microscope-js

Drop any of these onto the page — it renders locally:

  • 📄 *.pdf
  • 📝 *.docx   *.doc
  • 📊 *.xlsx   *.xls   *.csv   *.tsv   *.ods
  • 📽️ *.pptx
  • 🖼️ *.png *.jpg *.gif *.webp *.svg *.avif *.bmp
  • 🎞️ *.mp4 *.webm *.ogg *.mov *.mkv
  • 🎵 *.mp3 *.wav *.flac *.aac *.m4a *.opus
  • 💻 *.txt *.md *.json *.yaml and every common source-code extension

📦 Packages

Package Version Min+gzip Description
@microscope-js/core npm size Framework-agnostic registry + mount()
@microscope-js/utils npm size Source / MIME / sanitize helpers
@microscope-js/react npm size React / Next.js adapter, <Viewer />
@microscope-js/renderer-pdf npm size PDF via pdfjs-dist
@microscope-js/renderer-docx npm size Word .docx via mammoth + DOMPurify
@microscope-js/renderer-xlsx npm size Excel .xlsx/.xls/.csv via SheetJS
@microscope-js/renderer-pptx npm size PowerPoint .pptx via JSZip
@microscope-js/renderer-image npm size png / jpg / gif / webp / svg / avif / bmp
@microscope-js/renderer-video npm size mp4 / webm / ogg / mov
@microscope-js/renderer-audio npm size mp3 / wav / flac / ogg / aac
@microscope-js/renderer-text npm size Plain text / source code

🚀 Quick start

React / Next.js

pnpm add @microscope-js/react @microscope-js/renderer-pdf @microscope-js/renderer-image
'use client';
import { Viewer, useRegistry } from '@microscope-js/react';
import { pdfRenderer } from '@microscope-js/renderer-pdf';
import { imageRenderer } from '@microscope-js/renderer-image';

export default function FilePreview({ file }: { file: File }) {
  const registry = useRegistry([pdfRenderer, imageRenderer]);
  return <Viewer source={file} registry={registry} style={{ height: 600 }} />;
}

<Viewer /> accepts a File, Blob, ArrayBuffer, Uint8Array, URL, or a string URL — anything you have on hand.

Plain JS / Vue / Svelte / vanilla

import { createRegistry, mount } from '@microscope-js/core';
import { pdfRenderer } from '@microscope-js/renderer-pdf';

const registry = createRegistry([pdfRenderer]);
const handle = await mount({
  source: fileInput.files[0],
  container: document.getElementById('viewer')!,
  registry,
});
// later...
handle.destroy();

Hook-only (custom UI)

const { containerRef, loading, error, handle } = useViewer({ source, registry });
return (
  <div ref={containerRef} className="my-toolbar-frame">
    {handle?.capabilities?.pageCount /* renderer-specific extras */}
  </div>
);

🆚 How it compares

Concern MS Office iframe Google Docs viewer docx-preview / etc. microscope-js
File leaves the device ✅ uploads to Microsoft ✅ uploads to Google
Works offline partial
Formats covered Office only Office + PDF one per library PDF + Office + media + image + code, one API
Bundle cost iframe (heavy) iframe (heavy) one library, monolithic ~1–5 KB core + per-format packages, tree-shakable
React + Next.js / SSR ⚠️ iframe ⚠️ iframe varies ✅ first-class, SSR-safe
Provenance / signing n/a n/a usually unsigned ✅ SLSA + npm trusted publisher
License proprietary proprietary mixed MIT

🏗️ Architecture

                ┌─────────────────────────────────┐
                │   @microscope-js/core           │
                │   Renderer interface + registry │
                └──────────────┬──────────────────┘
                               │
       ┌───────────────────────┼──────────────────────────┐
       │                       │                          │
┌──────▼──────┐        ┌───────▼────────┐         ┌───────▼─────────┐
│ renderer-pdf│   ...  │ renderer-image │   ...   │ renderer-docx   │
└─────────────┘        └────────────────┘         └─────────────────┘
       │                       │                          │
       └───────────────────────┼──────────────────────────┘
                               │
                ┌──────────────▼──────────────────┐
                │   @microscope-js/react          │
                │   <Viewer />, useViewer()       │
                └─────────────────────────────────┘

The core package defines exactly one interface (Renderer) and one registry. Every format implementation lives in its own npm package and is loaded via dynamic import() so first-paint pays only for core + react.

🛡️ Security model

Threat Mitigation
HTML in Office documents All renderer output passes through DOMPurify with a hardened allowlist
Zip-slip (.. in DOCX/XLSX/PPTX) Archive entries with parent-dir or absolute paths are rejected before extraction
Zip bombs ByteBudget caps total uncompressed bytes (default 512 MB)
Oversized PDFs Configurable maxBytes / maxPages per renderer
SVG / XML XSS SVGs are mounted via <img> (no script execution); inline SVG is sanitized
Dynamic JS No eval, no new Function, no dangerouslySetInnerHTML of user content — CI greps for these
Supply-chain Every published tarball is signed with SLSA provenance via npm OIDC trusted publishing

See SECURITY.md to report a vulnerability privately.

🧪 Development

pnpm install
pnpm build           # build all packages
pnpm test            # vitest across the monorepo
pnpm typecheck       # tsc --noEmit, per package
pnpm lint            # biome check
pnpm demo            # localhost:3000
pnpm docs            # TypeDoc -> ./docs-site
microscope-js/
├── packages/
│   ├── core/         # Renderer interface + registry
│   ├── utils/        # source / sniff / sanitize / zip-safety helpers
│   ├── react/        # <Viewer /> + useViewer + useRegistry
│   ├── renderer-pdf/
│   ├── renderer-docx/
│   ├── renderer-xlsx/
│   ├── renderer-pptx/
│   ├── renderer-image/
│   ├── renderer-video/
│   ├── renderer-audio/
│   └── renderer-text/
├── apps/
│   └── demo/         # Next.js → GitHub Pages
└── .github/workflows/  # CI · release · pages · security · dependabot

🗺️ Roadmap

  • Pixel-perfect PPTX (shape + image layout, not just text)
  • EPUB / FB2 ebook renderer
  • RTF / ODT renderer
  • Optional Web Worker for office formats (offload main-thread parsing)
  • Plugin: in-viewer search across PDF / DOCX
  • Plugin: annotation / highlight overlay
  • Solid / Vue / Svelte adapters

Got an idea? Open a discussion.

🤝 Contributing

PRs are welcome — see CONTRIBUTING.md. Adding a new format is a copy of packages/renderer-image with a new Renderer object. The registry picks it up automatically.

If this saved you a backend, a star on GitHub goes a long way.

⭐ Star history

Star history chart

📄 License

MIT © microscope-js contributors

Built with pnpm · tsup · Changesets · Biome · Vitest · TypeDoc. Published with npm Trusted Publishers + SLSA provenance.

About

100% client-side file viewer for the web — render PDF, Office (DOCX/XLSX/PPTX), images, video, and audio in the browser. No server required.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors