{"id":51297056,"url":"https://github.com/harshpreet931/anyview","last_synced_at":"2026-06-30T15:32:26.416Z","repository":{"id":366819485,"uuid":"1277950679","full_name":"harshpreet931/anyview","owner":"harshpreet931","description":"View any document in React: PDF, DOCX, XLSX, PPTX, CSV, Markdown, code, HTML \u0026 images, rendered natively in the browser. No iframes, no uploads, files never leave the page.","archived":false,"fork":false,"pushed_at":"2026-06-23T12:12:45.000Z","size":280,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-23T13:34:50.931Z","etag":null,"topics":["csv","document-viewer","docx","file-viewer","markdown","office","pdf","pdf-viewer","pdfjs","pptx","react","react-component","typescript","viewer","xlsx"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/harshpreet931.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-23T10:28:42.000Z","updated_at":"2026-06-23T12:12:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/harshpreet931/anyview","commit_stats":null,"previous_names":["harshpreet931/anyview"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/harshpreet931/anyview","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harshpreet931%2Fanyview","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harshpreet931%2Fanyview/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harshpreet931%2Fanyview/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harshpreet931%2Fanyview/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/harshpreet931","download_url":"https://codeload.github.com/harshpreet931/anyview/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harshpreet931%2Fanyview/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34973611,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-30T02:00:05.919Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csv","document-viewer","docx","file-viewer","markdown","office","pdf","pdf-viewer","pdfjs","pptx","react","react-component","typescript","viewer","xlsx"],"created_at":"2026-06-30T15:32:25.760Z","updated_at":"2026-06-30T15:32:26.398Z","avatar_url":"https://github.com/harshpreet931.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"./assets/banner.png\" width=\"880\" alt=\"Anyview: one component, every format, zero iframes.\" /\u003e\n\n# Anyview\n\n### One component. Every format. Zero iframes.\n\nThe universal document viewer for React. It renders **PDF, DOCX, XLSX, PPTX, CSV, Markdown, code, HTML, Jupyter notebooks \u0026 images** natively in the browser. No iframes, no server round-trips, no uploads. Your files never leave the page.\n\n[![npm version](https://img.shields.io/npm/v/anyview?color=B8740F\u0026labelColor=1a1813)](https://www.npmjs.com/package/anyview)\n[![license](https://img.shields.io/github/license/harshpreet931/anyview?color=B8740F\u0026labelColor=1a1813)](./LICENSE)\n[![bundle size](https://img.shields.io/bundlephobia/minzip/anyview?color=B8740F\u0026labelColor=1a1813)](https://bundlephobia.com/package/anyview)\n[![CI](https://img.shields.io/github/actions/workflow/status/harshpreet931/anyview/ci.yml?branch=main\u0026labelColor=1a1813)](https://github.com/harshpreet931/anyview/actions)\n[![stars](https://img.shields.io/github/stars/harshpreet931/anyview?style=social)](https://github.com/harshpreet931/anyview)\n\n**[▶ Live demo](https://harshpreet931.github.io/anyview/)** \u0026nbsp;·\u0026nbsp; [Quick start](#quick-start) \u0026nbsp;·\u0026nbsp; [Why Anyview?](#why-anyview) \u0026nbsp;·\u0026nbsp; [FAQ](#faq)\n\n\u003cbr /\u003e\n\n\u003cimg src=\"./assets/demo.gif\" width=\"900\" alt=\"Anyview switching between PDF, DOCX, XLSX and Markdown while searching and annotating\" /\u003e\n\n\u003cbr /\u003e\u003cbr /\u003e\n\n\u003cimg src=\"./assets/screenshot.png\" width=\"900\" alt=\"Anyview rendering a real PDF with a thumbnail rail, search highlight, zoom, rotate, and annotation tools\" /\u003e\n\n\u003csub\u003eA real document rendered in the \u003ca href=\"https://harshpreet931.github.io/anyview/\"\u003elive playground\u003c/a\u003e, with a selectable text layer, cross-format search, thumbnails, and annotations.\u003c/sub\u003e\n\n\u003c/div\u003e\n\n---\n\n\u003e **Status:** `v0.2`, published on npm (`npm i anyview`), pre-1.0 and actively evolving. The flagship PDF experience (native render, text selection, search highlighting, 8 annotation tools) is implemented; reflowable formats (DOCX, XLSX, PPTX, CSV, Markdown, code, HTML, Jupyter notebooks) render, are searchable, and have page thumbnails. Full zoom UX (wheel / pinch / marquee / fit modes), two-page spread, a drag-drop empty state, and a rich imperative + callback API round it out. Try it via the [playground](#run-the-playground).\n\n## Why Anyview?\n\nEvery other React document viewer either **uses iframes** (slow, insecure, leaks your files to Microsoft/Google's online viewers, can't render private/auth-gated documents) or **supports only one format** (PDF-only, image-only). Anyview renders **everything natively in the browser**, with no iframes, no server-side conversion, and no clunky embeds. Private files stay client-side.\n\n| Feature | Anyview | @cyntler/react-doc-viewer | react-pdf | react-file-viewer |\n|---|:---:|:---:|:---:|:---:|\n| PDF | ✅ Native (pdf.js worker) | ✅ iframe | ✅ | ✅ |\n| DOCX | ✅ Native (mammoth.js) | ❌ iframe | ❌ | ✅ iframe |\n| XLSX | ✅ Native (SheetJS) | ❌ iframe | ❌ | ✅ iframe |\n| PPTX | ✅ Native (JSZip + XML) | ❌ | ❌ | ❌ |\n| CSV/TSV | ✅ Native (PapaParse) | ❌ | ❌ | ✅ |\n| Markdown | ✅ Native (react-markdown) | ❌ | ❌ | ✅ |\n| Code (50+ langs) | ✅ Native (Shiki) | ❌ | ❌ | ❌ |\n| HTML | ✅ Sanitized (DOMPurify) | ✅ iframe | ❌ | ✅ iframe |\n| Jupyter (`.ipynb`) | ✅ Native | ❌ | ❌ | ❌ |\n| Images (8 formats) | ✅ Native | ✅ | ❌ | ✅ |\n| Private files (no upload) | ✅ | ❌ public URL only | ✅ | ❌ |\n| Text selection | ✅ | partial | ✅ | ❌ |\n| Search + highlight | ✅ all formats | ❌ | ✅ PDF | ❌ |\n| Annotations | ✅ 8 tools (PDF) | ❌ | ❌ | ❌ |\n| Zoom (wheel/pinch/marquee/fit) | ✅ | ❌ | partial | ❌ |\n| Two-page spread | ✅ | ❌ | ❌ | ❌ |\n| **No iframes** | ✅ | ❌ | ✅ | ❌ |\n| **Web Workers** | ✅ Off-main-thread | ❌ | ✅ | ❌ |\n| **Tree-shakeable** | ✅ Load only what you need | ❌ | ✅ | ❌ |\n| **WCAG 2.2 AA** | ✅ Built-in | ❌ | ❌ | ❌ |\n| **Bundle size** | **~23 kB gzip** (base) | ~40 kB | ~50 kB | ~100 kB |\n\n## Quick Start\n\n```bash\nnpm install anyview\n```\n\nAnyview keeps format parsers as **optional peer dependencies** so you ship only what you use. Add the parser for each format you need:\n\n```bash\nnpm install pdfjs-dist                  # PDF\nnpm install mammoth                     # DOCX\nnpm install xlsx                        # XLSX\nnpm install jszip                       # PPTX\nnpm install papaparse                   # CSV/TSV\nnpm install react-markdown remark-gfm   # Markdown\nnpm install shiki                       # code highlighting\nnpm install dompurify                   # HTML / DOCX sanitization\n```\n\n\u003e Jupyter notebooks (`.ipynb`) reuse the Markdown, code, and sanitizer parsers above, so they need no extra dependency.\n\n```tsx\nimport { DocViewer } from 'anyview';\nimport 'anyview/styles';\n\nfunction App() {\n  return (\n    \u003cDocViewer\n      source={{ kind: 'url', url: '/files/report.pdf' }}\n      theme=\"auto\"\n      showToolbar\n      showSidebar\n      onDocumentLoad={(doc) =\u003e console.log(`${doc.pageCount} pages`)}\n    /\u003e\n  );\n}\n```\n\n`source` accepts a `File`, a URL, a raw `ArrayBuffer`, or a `FileSystemFileHandle`:\n\n```tsx\n\u003cDocViewer source={{ kind: 'file', file }} /\u003e\n\u003cDocViewer source={{ kind: 'buffer', buffer, name: 'a.docx', type: '' }} /\u003e\n```\n\n## Headline features\n\n- **Native rendering, zero iframes.** PDF rasterizes off the main thread in a Web Worker (OffscreenCanvas → `ImageBitmap`), while Office, text, and notebook formats parse to real DOM.\n- **Real text selection.** Select and copy text directly off PDF pages via an aligned, invisible text layer over the bitmap; reflowable formats select natively.\n- **Search with highlighting.** `Cmd/Ctrl+F` finds matches across **every text format**, paints highlight overlays, distinguishes the active match, and scrolls it into view. PDF uses the text layer; DOCX, XLSX, PPTX, Markdown, code, HTML, CSV, and notebooks use the CSS Custom Highlight API over real DOM text.\n- **Full zoom \u0026 layout.** Ctrl/Cmd+wheel and two-finger **pinch** zoom (anchored at the cursor), **marquee** zoom-to-selection, **fit-page / fit-width / actual-size** modes, and a **two-page / book-spread** layout.\n- **Annotations, 8 tools.** Highlight, free-hand ink, sticky note, **rectangle, ellipse, line, arrow, and free-text** on PDF pages. Geometry is normalized so they survive zoom. Create, read, update, and delete them, **export/import as versioned sidecar JSON** through the imperative ref, and observe changes with `onAnnotationChange`.\n- **Jupyter notebooks.** Open `.ipynb` files natively: markdown cells, syntax-highlighted code, and saved outputs (text, images, HTML/SVG, error tracebacks). No kernel, no Python, no upload.\n- **Drop-in file opening.** The empty state is a working dropzone, so you can click to browse or drop a file to open it.\n- **Events \u0026 control.** Rich callbacks (`onPageChange`, `onZoom`, `onSearchResult`, `onVisiblePagesChange`, `onSelectionChange`, `onAnnotationChange`) plus a controlled `page` prop.\n- **Virtualized.** Only visible pages (± overscan) are in the DOM, and a byte-budgeted LRU cache keeps rendered bitmaps under a 256 MB ceiling.\n- **Accessible, themeable \u0026 SSR-ready.** ARIA roles, keyboard nav, forced-colors and reduced-motion support, four themes (`light` / `dark` / `auto` / `sepia`), full i18n via the `locale` prop, and Next.js App Router support (ships `'use client'`, SSR-safe).\n\n## Supported Formats\n\n| Format | Engine | Worker | Text select | Search | Outline | Annotations |\n|---|---|:---:|:---:|:---:|:---:|:---:|\n| PDF | pdf.js | ✅ | ✅ | ✅ | ✅ | ✅ |\n| DOCX | mammoth.js | Planned | ✅ | ✅ | ✅ | - |\n| XLSX | SheetJS | Planned | ✅ | ✅ | ✅ | - |\n| PPTX | JSZip + XML | Planned | ✅ | ✅ | ✅ | - |\n| CSV/TSV | PapaParse | Planned | ✅ | ✅ | - | - |\n| Markdown | react-markdown + remark-gfm | - | ✅ | ✅ | ✅ | - |\n| Code (50+) | Shiki | - | ✅ | ✅ | - | - |\n| HTML | DOMPurify | - | ✅ | ✅ | ✅ | - |\n| Jupyter (`.ipynb`) | react-markdown + Shiki | - | ✅ | ✅ | ✅ | - |\n| Images (8) | Native browser | - | - | - | - | - |\n| Plain Text | Native | - | ✅ | ✅ | - | - |\n\n\u003e \"Worker: Planned\" means the format currently parses on the main thread; the architecture supports moving it to a worker without API changes.\n\n## Run the playground\n\n```bash\npnpm install\npnpm --filter anyview-playground dev\n```\n\nDrag any PDF / DOCX / XLSX / PPTX / CSV / Markdown / code / HTML / `.ipynb` / image onto the page. It renders entirely client-side; nothing is uploaded.\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────┐\n│                   DocViewer                       │\n│  ┌─────────┐  ┌──────────┐  ┌─────────────────┐  │\n│  │ Toolbar │  │ Sidebar  │  │  Viewer         │  │\n│  │ (zoom,  │  │ (thumbs, │  │  (virtualized   │  │\n│  │  search,│  │  outline,│  │  page list +    │  │\n│  │  annot) │  │  attach) │  │  text/search/   │  │\n│  └─────────┘  └──────────┘  │  annot layers)  │  │\n│                             └─────────────────┘  │\n│              ┌─────────────────┐                  │\n│              │  Zustand Store  │                  │\n│              │  (6 slices)     │                  │\n│              └────────┬────────┘                  │\n│              ┌────────┴────────┐                  │\n│              │ Adapter Registry│ (fresh instance  │\n│              │  (plugin arch)  │  per document)   │\n│              └────────┬────────┘                  │\n│     ┌──────────┬──────┴───┬──────────┐            │\n│     ▼          ▼          ▼          ▼            │\n│  ┌─────┐   ┌─────┐    ┌─────┐    ┌─────┐          │\n│  │ PDF │   │DOCX │    │XLSX │    │ ... │          │\n│  │worker│  │     │    │     │    │     │          │\n│  └─────┘   └─────┘    └─────┘    └─────┘          │\n│  pdfjs-dist  mammoth   xlsx       ...             │\n│  (dynamic import, never in the base bundle)       │\n└─────────────────────────────────────────────────┘\n```\n\n### Key design decisions\n\n- **Plugin architecture.** Each format is a self-contained adapter with a static manifest (loaded synchronously, so the toolbar knows capabilities before any parser loads) and a lazy loader (dynamic `import()` only when a matching file opens). The registry constructs a **fresh adapter instance per document**, so multiple viewers never share state.\n- **Worker-offloaded.** PDF parsing and rendering run in a Web Worker via Comlink, so the main thread never blocks.\n- **Byte-budgeted LRU cache.** Rendered page bitmaps are cached under a 256 MB budget with automatic eviction and memory-pressure shrinking.\n- **WCAG 2.2 AA.** ARIA roles, keyboard navigation, screen-reader announcements, forced-colors, and reduced-motion support, built in.\n- **~23 kB gzip base.** Only Zustand + Comlink + @tanstack/react-virtual ship in the base; format engines load on demand as separate chunks.\n\n## Advanced Usage\n\n### Custom adapter\n\nAdapters are loaded as **classes**: the module's default export is a constructor the registry instantiates per document.\n\n```tsx\nimport { DocViewer, createRegistry, registerBuiltInAdapters } from 'anyview';\n\nconst registry = createRegistry();\nregisterBuiltInAdapters(registry);\n\nregistry.register(\n  {\n    id: 'epub',\n    label: 'EPUB Book',\n    extensions: ['epub'],\n    mimeTypes: ['application/epub+zip'],\n    icon: '\u003csvg\u003e…\u003c/svg\u003e',\n    features: { search: true, annotations: false, textSelection: true, /* … */ },\n    priority: 100,\n    protocolVersion: 1,\n  },\n  () =\u003e import('./adapters/epub'), // default-exports the EpubAdapter class\n);\n\nfunction App() {\n  return \u003cDocViewer source={source} registry={registry} /\u003e;\n}\n```\n\n### Imperative API\n\n```tsx\nconst ref = useRef\u003cDocViewerRef\u003e(null);\n\nref.current?.goToPage(5);\nref.current?.zoomIn();\nref.current?.search({ text: 'invoice', caseSensitive: false, wholeWord: false, regex: false, diacritics: false });\nref.current?.nextMatch();\nawait ref.current?.print();\n\n// Programmatic annotations for review / markup flows:\nref.current?.setActiveTool('rectangle');\nref.current?.addAnnotation(annotation);\nconst sidecar = ref.current?.exportAnnotations();   // { version: 1, annotations: [...] }\nref.current?.importAnnotations(sidecar);            // a sidecar object or its JSON string\n```\n\n### Events \u0026 controlled props\n\n```tsx\n\u003cDocViewer\n  source={source}\n  page={page}                                  // controlled: drives the current page\n  onPageChange={(page, total) =\u003e setPage(page)}\n  onZoom={(zoom, fitMode) =\u003e {}}\n  onSearchResult={(result) =\u003e {}}\n  onVisiblePagesChange={(pages) =\u003e {}}          // e.g. lazy-load thumbnails\n  onSelectionChange={({ text, pageIndex }) =\u003e {}}\n/\u003e\n```\n\n### Annotations\n\nEight tools are available: **highlight, ink, sticky note, rectangle, ellipse, line, arrow, and free-text**. Geometry is normalized so annotations survive zoom. Persist them as versioned sidecar JSON, or drive them through the [imperative API](#imperative-api) (`addAnnotation` / `setAnnotations` / `exportAnnotations` / `importAnnotations`).\n\n```tsx\nimport { serializeAnnotations, parseAnnotations } from 'anyview';\n\n\u003cDocViewer\n  source={source}\n  onAnnotationChange={(annotations) =\u003e localStorage.setItem('notes', serializeAnnotations(annotations))}\n/\u003e\n```\n\n### Hooks \u0026 i18n\n\n```tsx\nimport { useNavigation, I18nProvider, registerStrings } from 'anyview';\n\nfunction PageIndicator() {\n  const { currentPage, totalPages } = useNavigation();\n  return \u003cspan\u003e{currentPage + 1} / {totalPages}\u003c/span\u003e;\n}\n\n// Localize: register a strings table, then pass locale=\"fr\"\nregisterStrings('fr', frenchStrings);\n\u003cDocViewer source={source} locale=\"fr\" /\u003e\n```\n\n## Theming\n\n```css\n.my-app .dv-root {\n  --dv-toolbar-height: 40px;\n  --dv-progress-bar-color: #e63946;\n  --dv-sidebar-width: 240px;\n}\n```\n\nFour built-in themes: `light`, `dark`, `auto` (follows system), `sepia`.\n\n## Browser Support\n\n| Feature | Chrome 90+ | Firefox 88+ | Safari 15+ | Edge 90+ |\n|---|:---:|:---:|:---:|:---:|\n| Core viewer | ✅ | ✅ | ✅ | ✅ |\n| Web Workers / OffscreenCanvas | ✅ | ✅ | 17+ | ✅ |\n| CSS Custom Highlight (DOM search) | ✅ | ✅ | 17.2+ | ✅ |\n| File System Access | ✅ | - | - | ✅ |\n\nMissing features degrade gracefully. For example, rendering falls back to a main-thread canvas without OffscreenCanvas, and search still counts and navigates matches where the Custom Highlight API is unavailable.\n\n## Development\n\n```bash\npnpm install\npnpm lint        # eslint\npnpm typecheck   # tsc --noEmit\npnpm test        # vitest\npnpm build       # vite library build\n```\n\n## FAQ\n\n**How do I view a DOCX / XLSX / PPTX file in React without an iframe?**\nUse Anyview. It renders Office formats **natively in the browser** (DOCX via mammoth.js, XLSX via SheetJS, PPTX via JSZip), so there is no iframe and no Microsoft/Google Online viewer involved:\n\n```tsx\nimport { DocViewer } from 'anyview';\nimport 'anyview/styles';\n\n\u003cDocViewer source={{ kind: 'file', file }} /\u003e   // a File from an \u003cinput\u003e, drag-drop, etc.\n```\n\n**Can it render private or local files (not just public URLs)?**\nYes. Everything runs client-side, so private, authenticated, and local files work and **nothing is ever uploaded**. This is the main difference from iframe-based viewers like `@cyntler/react-doc-viewer` / `react-doc-viewer`, which render Office files through the Microsoft Office Online service and therefore **require publicly accessible URLs** and send your documents to a third party.\n\n**How do I render any document type in React with one component?**\n`\u003cDocViewer source={…} /\u003e` auto-detects the format from the file name / MIME type and loads the matching renderer on demand: PDF, DOCX, XLSX, PPTX, CSV, Markdown, code, HTML, Jupyter notebooks, and images, all from one component.\n\n**Is there a free, open-source alternative to PSPDFKit / Nutrient / Apryse for viewing Office documents in React?**\nYes. Anyview is MIT-licensed and free. It renders PDF and Office formats natively (no server, no license key) with text selection, cross-format search, and PDF annotations.\n\n**Which packages do I need to install?**\nJust `anyview` plus the parser for each format you actually use (they're optional peer dependencies): `pdfjs-dist` for PDF, `mammoth` for DOCX, `xlsx` for XLSX, `jszip` for PPTX, `papaparse` for CSV, `react-markdown` + `remark-gfm` for Markdown, `shiki` for code, `dompurify` for HTML. You ship only what you use.\n\n**Does it work with Next.js / Vite / CRA?**\nYes. It's a standard React component (React 18+). For SSR frameworks like Next.js, render it client-side (the parsers use browser APIs and a Web Worker).\n\n## Contributing\n\nContributions are welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md).\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharshpreet931%2Fanyview","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fharshpreet931%2Fanyview","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharshpreet931%2Fanyview/lists"}