office-open-xml-viewer renders Word, Excel and PowerPoint files in a browser
A browser-based viewer for Office Open XML documents that renders to an HTML Canvas element.
At a glance
- What is it?
- The Office Open XML Viewer parses docx, xlsx and pptx files with Rust code compiled to WebAssembly and paints them onto a canvas, so a web application can display Office documents without Office. It is a read-only viewer, its whole codebase was written by AI coding agents, and the version numbers are still in the 0.8x range.
- Who is it for?
- Adopt the Office Open XML Viewer when a web application has to display Office documents, especially when you want the document model, not a picture of it, and when a read-only view is enough. Do not adopt it for editing, saving or round-tripping documents, because the project rules all of that out of scope, and do not adopt it on the assumption that the rendering matches Office exactly, because the README frames the fidelity as the thing still being refined.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Office documents in a canvas, without Office
The Office Open XML Viewer is a browser-based viewer for Office Open XML documents that renders to an HTML Canvas element. The npm package is @silurus/ooxml, the licence is MIT, and the live demo at ooxml.silurus.dev shows the three supported formats side by side.
The reason the project exists is stated at length by its author, and the reason is worth reading because it explains the shape of the code. OOXML behaviour is defined by a written specification, ECMA-376 and ISO-29500, and there is a clear answer to compare against: Word, Excel and PowerPoint themselves. In principle anyone with enough patience could build a faithful viewer, but in practice that did not happen. For more than a decade, no free, open source library reached a rendering quality good enough for real use. There are a few commercial libraries with decent fidelity, and with editing support, but their pricing makes them hard to adopt casually. The author's conclusion is that the reason is cost: the specification is huge.
The scope decision follows from the same analysis. Limiting the library to viewing avoids the most serious risk an Office library can carry, which is corrupting a user's files. The README says the project is intentionally a read-only viewer, and that editing interfaces, mutation APIs, editable or lossless document models, saving, round-tripping and partial editing of DOCX, XLSX or PPTX are out of scope. Read-only interactions stay in scope: selection, copy, search, shape IDs, hit testing, and annotation or external tool integrations. The rationale is recorded in issue 496.
For a team shipping a document preview, that scope is a feature rather than a limitation, because a viewer that cannot write cannot damage a file.
Rust parsers in WebAssembly, Canvas 2D for the pixels
The architecture is a two language split. The parsers are written in Rust and compiled to WebAssembly, and the renderers use the Canvas 2D API. The npm description says the same thing in a line: a browser-based OOXML viewer for docx, xlsx and pptx, with a Rust and WASM parser plus a Canvas renderer.
The reason for Rust here is the document format itself. An OOXML file is a zip archive of XML parts, and reading it means unzipping it, walking the relationships between parts and parsing XML that a generic parser would be slow on. The Cargo workspace shows the shape: a common package plus a model package and a parser package for each of the three formats, an optional legacy Office parser, and an mcp-server package. The dependency list is the standard set for that job, with flate2 for deflate, zip, roxmltree and quick-xml for XML, serde for the models, and wasm-bindgen for the bridge to JavaScript.
The release profile in Cargo.toml is tuned for the browser rather than for a server. opt-level is s, lto is on, codegen-units is set to 1 so LLVM can optimise and dead-strip across the whole crate rather than per unit, panic is set to abort because WebAssembly has no stack unwinding, and debuginfo is stripped so the .wasm that ships to browsers stays small.
The rendering target is a canvas rather than the DOM, which is what makes the headless engine possible. A canvas is an object you can hand to the library, and the library paints into it.
The headless engine versus the built-in viewer
Each format exposes two levels of API, and the difference is the one to understand before you build on it.
The first is the viewer. DocxViewer takes the canvas you provide, loads a document and gives you page navigation, XlsxViewer manages its own canvas and tab bar, and PptxViewer does the same for presentations. That is the built-in UI, and it is the quickest way to get something on screen.
The second is the headless engine, named DocxDocument, XlsxWorkbook and PptxPresentation. These render into any caller-supplied canvas, so you can compose your own interface, scroll views, thumbnail grids, master-detail panes, instead of being locked into the built-in viewer.
The quick start shows the difference in practice:
import { DocxViewer } from '@silurus/ooxml/docx';
import { XlsxSheetViewer, XlsxViewer, XlsxWorkbook } from '@silurus/ooxml/xlsx';
import { PptxViewer } from '@silurus/ooxml/pptx';
const docxCanvas = document.getElementById('docx-canvas') as HTMLCanvasElement;
const docx = new DocxViewer(docxCanvas);
await docx.load('/document.docx');The Excel case shows a detail that will matter if you build a grid on top of it. The viewer manages its own canvas and tab bar, and selection is set separately from the active cell: xlsx.setSelection('B2:D5') describes the geometry, and the normalised upper-left is the active cell. When the active cell or the Shift-extension anchor is not the upper-left of the area, you pass structured state instead, with areas, activeAreaIndex, activeCell and extensionAnchor.
The repository carries runnable framework examples for React, Vue, Svelte and Solid, which is the fastest way to see how the headless engine fits a real application.
Installing the package and the bundler traps
The package is on npm, and the install is one command:
npm install @silurus/ooxmlor, with pnpm:
pnpm add @silurus/ooxmlThe interesting part is what follows, because the Rust parsers ship as real .wasm asset files next to the JavaScript, referenced with the standard new URL('…', import.meta.url) form and fetched, streaming-compiled, at load time. The README lists the setups verified to work with zero configuration: webpack 5, Next.js with Turbopack in development and build, Vite 8 in development and build, Vite 7 production builds, and a plain script tag of type module with no bundler at all.
Two setups need a hand, and knowing which you are saves an afternoon.
On the Vite 7 development server, the dependency optimizer rewrites the asset reference into its own cache path and the load fails. That is fixed in Vite 8, and the workaround is to add optimizeDeps: { exclude: ['@silurus/ooxml'] } to your vite.config. Production builds are unaffected.
On esbuild and the Angular CLI, whose application builder is esbuild based, new URL asset references are not processed, which is a known esbuild issue. There you copy the .wasm into your served output and point the viewer at it with the wasmUrl load option:
new DocxViewer(canvas, { wasmUrl: 'https://cdn.example.com/docx_parser_bg.wasm' });The same option serves the parser from a CDN or any path you control, which is the answer for teams that do not want the .wasm in their own bundle at all.
An AI-written codebase, and what that means for a parser
The note at the top of the README is unusual enough to quote. The entire codebase, Rust parsers, TypeScript renderers, tests and tooling, is implemented by AI coding agents, primarily Claude and Codex, through iterative prompting. No human-written application code exists in the repository.
The author explains why this is a fit for the problem rather than a shortcut. A viewer is an unusually good match for AI-driven iterative development, because there is a specification to read and a correct output to aim for. The work becomes interpreting the specification and refining the rendering until it matches. The comparison target is the desktop Office applications, and in the author's words, for some documents it already reproduces them more faithfully than commercial libraries, and sometimes even the official Microsoft 365 web apps.
That claim needs the right frame. It is a claim about rendering fidelity on some documents, produced by the project author, and the honest reading is that fidelity is the ongoing work rather than a finished property. A viewer that mostly matches is genuinely useful; a viewer that matches on every file you care about is something you have to check yourself, which is why the live demo and your own worst-case document are the right first test.
The repository layout reflects a spec-first, quality-gated approach. There are rule directories and rule-tests, a fuzz directory, a Storybook setup for component work, a site directory for the documentation, an examples directory for the framework integrations, and both AGENTS.md and CLAUDE.md files, which are the instructions the coding agents work from.
Bundle size, optional renderers and the format entry points
The package is ESM-only, in .mjs files, and the manifest says sideEffects false. The bundle size note in the README is worth reading carefully, because npm's Unpacked Size figure is misleading for this shape of package: it includes every entry point and every optional asset, so it is not the size of a format-specific application graph.
The guidance is to import only the format and optional renderer entries your application needs. In main mode, the MathJax, ChartEx, 3-D, Region Map and TIFF implementations remain outside the graph unless you import them, and the separately loaded worker asset is self-contained.
The export map is what makes that possible. There is a root entry, and then separate entries for docx, xlsx, pptx, math, three-d and region-map, each with its own types and its own .mjs file, so a bundler can tree-shake by format. The exports are published with public access and provenance enabled, and the published files are the dist directory and the third party notices file.
The Rust side has its own version of the same idea. The workspace separates the common package, the model packages and the parser packages per format, and the release profile is set to optimise across the whole crate rather than per unit, which trades compile time for a smaller and faster .wasm.
A third piece sits outside the npm package entirely: an optional, experimental legacy Office add-on that reads a supported subset of .doc, .xls and .ppt with the same viewers, documented separately on the project site.
Versions, licence and the shape of the project
The release history moves quickly. Version 0.88.0 was published on 2026-09-21, 0.89.0 on 2026-10-19 and 0.89.1 later the same day, and the last push to the main branch was on 2026-10-19. The repository is not archived.
Those version numbers are the thing to notice. A project in the 0.8x range publishes a minor version often, and in a normal package that cadence signals an unstable API. Here it reflects a project that is still changing shape, and the manifest and README move with it. For a team pinning this dependency, that means the pin is the contract: the version you choose is the API you get, and the export map, the load options and the WASM asset layout are all things to check again at each upgrade.
The licence is MIT, with a THIRD_PARTY_NOTICES.md file published alongside the package, which is the right thing for a library that bundles parsers and renderers built on other people's work. The commercial libraries the author compares against are the alternative for a team that needs editing support or accepts a licence fee; the difference in approach is that this one is free, read-only and open, and the rendering is done by your own browser on your own machine rather than by a hosted service, which matters for documents you cannot upload anywhere.
That last point is the one to test first. Take a document you would never send to a third party, open it in the live demo, and see whether the result is good enough for your use.
Editorial conclusion
Adopt the Office Open XML Viewer when a web application has to display Office documents, especially when you want the document model, not a picture of it, and when a read-only view is enough. Do not adopt it for editing, saving or round-tripping documents, because the project rules all of that out of scope, and do not adopt it on the assumption that the rendering matches Office exactly, because the README frames the fidelity as the thing still being refined. Before you commit, try the live demo at ooxml.silurus.dev with your own worst-case file, and check your bundler against the documented exceptions for Vite 7 in development mode and for esbuild-based setups, where the .wasm asset has to be copied by hand.
Frequently asked questions
How do I install the Office Open XML Viewer?
Run npm install @silurus/ooxml, or pnpm add @silurus/ooxml. The package is ESM-only and exposes separate entry points for docx, xlsx, pptx, math, three-d and region-map, so you can import only the formats you need.
Which bundlers does the @silurus/ooxml package support?
The README says it is verified to work with zero configuration on webpack 5, Next.js with Turbopack, Vite 8, Vite 7 production builds and a plain module script. Vite 7 in development needs optimizeDeps exclude for the package, and esbuild-based setups such as the Angular CLI must copy the .wasm file and pass it with the wasmUrl option.
Can the Office Open XML Viewer edit or save documents?
No. The project is intentionally a read-only viewer, and editing interfaces, mutation APIs, lossless document models, saving and round-tripping are explicitly out of scope, while selection, copy, search, shape IDs, hit testing and annotations remain in scope.
What is the difference between DocxViewer and DocxDocument?
DocxViewer is the built-in viewer that takes a canvas and provides the interface, while DocxDocument, XlsxWorkbook and PptxPresentation are headless engines that render into any canvas the caller supplies, so you can build your own UI with scroll views, thumbnail grids or master-detail panes.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/yukiyokotani-office-open-xml-viewer)Was this useful?