···6677## Features
8899-- **Multi-format rendering** — PDF, EPUB, DOCX, CSV/TSV, source code (100+ languages), plain text
99+- **Multi-format rendering** — PDF, EPUB, DOCX, ODT, ODS, CSV/TSV, source code (100+ languages), plain text, and comic book archives (.cbz, .cbr, .cb7, .cbt)
1010- **Chunked loading** — Stream large documents via pre-rendered page images or split PDF chunks
1111- **Fetch adapters** — Pass data directly or provide a lazy-loading callback for on-demand fetching
1212- **CSS variable theming** — Dark and light themes built in, fully customizable via `--dv-*` variables
1313- **Framework-agnostic** — Use vanilla JS, React, or build your own wrapper
1414- **Lazy peer dependencies** — Only loads renderer libraries (pdfjs, epubjs, etc.) when that format is actually used
1515- **Custom renderers** — Register your own renderer for any format via the plugin registry
1616+- **Word wrap / fit toggle** — Toolbar button to toggle word wrap on code/text files and fit-to-width on comic pages
1617- **TypeScript-first** — Complete type definitions for all APIs
17181819## Installation
···3031npm install docx-preview # DOCX
3132npm install papaparse # CSV/TSV
3233npm install highlight.js # Code syntax highlighting
3333-npm install jszip # ODT
3434+npm install jszip # ODT, CBZ comic archives
3435npm install xlsx # ODS
3636+npm install papaparse # CSV/TSV
3737+npm install highlight.js # Code syntax highlighting
3838+3939+# Comic book archives — additional optional backends:
4040+npm install node-unrar-js # CBR (.cbr, RAR-compressed comics)
4141+npm install 7z-wasm # CB7 (.cb7, 7-Zip-compressed comics)
4242+4343+# Comic book archives — optional exotic image format decoders:
4444+npm install @jsquash/jxl # JPEG XL images inside archives
4545+npm install utif # TIFF images inside archives
3546```
36473748You only need to install peer dependencies for the formats you plan to render. Unused formats won't add to your bundle.
···388399| XML/HTML | `highlight.js` | `.xml`, `.html`, `.svg` |
389400| Pages | _(none)_ | N/A (explicit `type: 'pages'`) |
390401| Chunked PDF | `pdfjs-dist` | N/A (explicit `type: 'chunked'`) |
402402+| Comic — CBZ | `jszip` | `.cbz` |
403403+| Comic — CBR | `node-unrar-js` _(optional)_ | `.cbr` |
404404+| Comic — CB7 | `7z-wasm` _(optional)_ | `.cb7` |
405405+| Comic — CBT | _(none, built-in TAR reader)_ | `.cbt` |
406406+| Comic — CBA | ❌ not supported | `.cba` |
391407392408## Browser Support
393409···418434│ │ ├── ods.ts # ODS (xlsx)
419435│ │ ├── csv.ts # CSV/TSV (papaparse)
420436│ │ ├── code.ts # Code (highlight.js)
421421-│ │ └── text.ts # Plain text
437437+│ │ ├── text.ts # Plain text
438438+│ │ └── comic.ts # Comic book archives (jszip / node-unrar-js / 7z-wasm)
422439│ └── package.json
423440└── react/ @polyrender/react — React wrapper
424441 ├── src/
+2
examples/basic/README.md
···2828- Auto-detecting the document format from the filename
2929- Rendering with the built-in dark-themed toolbar
3030- Configuring the `pdfjs-dist` worker URL via Vite's `?url` import
3131+- Comic book archives (`.cbz`, `.cbr`, `.cb7`, `.cbt`) with JPEG XL and TIFF support enabled via `@jsquash/jxl` and `utif`
3232+- Word wrap / fit-to-width toolbar toggle (active for code, text, and comic files)
···1717 'highlight.js',
1818 'jszip',
1919 'xlsx',
2020+ 'node-unrar-js',
2121+ '@jsquash/jxl',
2222+ 'utif',
2023 ]
21242225 // Build a code snippet that maps module names → static imports.
···2528 .map((d) => ` case '${d}': return import('${d}').then(m => m.default || m);`)
2629 .join('\n')
27303131+ // 7z-wasm's default ESM build imports Node's 'module' built-in, so we
3232+ // redirect to its UMD build which is browser-safe.
3333+ const sevenZipCase = ` case '7z-wasm': return import('7z-wasm/7zz.umd.js').then(m => m.default || m);`
3434+2835 const replacement = [
2936 '(async (name) => { switch(name) {',
3037 cases,
3838+ sevenZipCase,
3139 ' default: throw new Error(`Unknown peer dep: ${name}`);',
3240 ' }})(moduleName)',
3341 ].join('\n')
···53615462export default defineConfig({
5563 plugins: [resolvePeerDeps()],
6464+ // @jsquash/jxl ships a Web Worker whose internal format is "iife".
6565+ // Forcing workers to ES module format prevents the Rollup error:
6666+ // "Invalid value 'iife' for option 'worker.format' — UMD and IIFE output
6767+ // formats are not supported for code-splitting builds."
6868+ worker: {
6969+ format: 'es',
7070+ },
5671 optimizeDeps: {
5772 include: [
5873 'pdfjs-dist',
···6277 'highlight.js',
6378 'jszip',
6479 'xlsx',
8080+ 'node-unrar-js',
8181+ '7z-wasm/7zz.umd.js',
8282+ '@jsquash/jxl',
8383+ 'utif',
6584 ],
6685 },
6786})
+8
examples/vanilla/README.md
···2020```bash
2121pnpm dev
2222```
2323+2424+## What it shows
2525+2626+- Opening a local file via a file input (PDF, EPUB, DOCX, CSV, code, comic archives, and more)
2727+- Auto-detecting the document format from the filename
2828+- Rendering with the built-in dark-themed toolbar
2929+- Comic book archives (`.cbz`, `.cbr`, `.cb7`, `.cbt`) with JPEG XL and TIFF support enabled via `@jsquash/jxl` and `utif`
3030+- Word wrap / fit-to-width toolbar toggle (active for code, text, and comic files)
+31-1
examples/vanilla/build.js
···1616const __dirname = dirname(fileURLToPath(import.meta.url));
1717const isWatch = process.argv.includes("--watch");
18181919+/**
2020+ * Plugin that provides empty shims for Node.js built-in modules when bundling
2121+ * for the browser. Some WASM-based packages (e.g. 7z-wasm) include conditional
2222+ * Node code paths that are never reached in a browser, but esbuild still tries
2323+ * to resolve the `require("fs")` / `require("crypto")` calls at bundle time.
2424+ */
2525+const shimNodeBuiltins = {
2626+ name: "shim-node-builtins",
2727+ setup(build) {
2828+ const builtins =
2929+ /^(fs|crypto|path|os|module|stream|util|events|buffer|assert|http|https|net|tls|url|zlib|readline|child_process|worker_threads|perf_hooks)$/;
3030+ build.onResolve({ filter: builtins }, (args) => ({
3131+ path: args.path,
3232+ namespace: "node-builtin-shim",
3333+ }));
3434+ build.onLoad({ filter: /.*/, namespace: "node-builtin-shim" }, () => ({
3535+ contents: "module.exports = {}",
3636+ loader: "js",
3737+ }));
3838+ },
3939+};
4040+1941/** Plugin to resolve @polyrender/core's dynamic peer-dep imports. */
2042const resolvePeerDeps = {
2143 name: "resolve-peer-deps",
···3759 "highlight.js",
3860 "jszip",
3961 "xlsx",
6262+ "node-unrar-js",
6363+ "@jsquash/jxl",
6464+ "utif",
4065 ];
6666+ // 7z-wasm's default ESM build imports Node's 'module' built-in,
6767+ // so we redirect to its UMD build which is browser-safe.
6868+ const sevenZipCase = ` case '7z-wasm': return import('7z-wasm/7zz.umd.js').then(m => m.default || m);`;
4169 const cases = peerDeps
4270 .map(
4371 (d) =>
···4876 const replacement = [
4977 "(async (name) => { switch(name) {",
5078 cases,
7979+ sevenZipCase,
5180 " default: throw new Error(`Unknown peer dep: ${name}`);",
5281 " }})(moduleName)",
5382 ].join("\n");
···100129 entryPoints: [resolve(__dirname, "src/main.ts")],
101130 bundle: true,
102131 format: "esm",
132132+ platform: "browser",
103133 outdir: distDir,
104134 sourcemap: true,
105135 target: "es2022",
106106- plugins: [resolvePeerDeps],
136136+ plugins: [shimNodeBuiltins, resolvePeerDeps],
107137 logLevel: "info",
108138};
109139