Skip to content
Apertura
Content extraction and citation anchorsComing soon

Office documents, rendered in the browser

Apertura opens .docx, .xlsx and .pptx directly in the page. No conversion service, no upload, no runtime dependencies — the file is parsed and drawn in the same tab that is showing it to you.

Get started$npm i @apertura/react
sample.docx
0 requests · nothing uploaded
Loading the viewer…
formatstatusidleopened inrendered bythis browser

This is not a screenshot. Open your own file and watch the network tab stay empty.

~1,500
Documents in the corpus
restored from source, never committed
79.3%
Formula cells agreeing with Excel
of comparable cells
0
Runtime dependencies
in every package
733
Tests
run on every change
The alternative

You are probably converting to PDF on a server

It works, and it costs you a service to run and scale, a licence to renew, a derivative to store, and a copy of every confidential document sitting somewhere it did not need to be.

 A browser libraryBackend conversionApertura
Where it runsBrowserA conversion service you run and scaleBrowser
Where the document goesNowhereUploaded, converted, cached as a derivativeNowhere
Page breaksWord’s cached lastRenderedPageBreak hintsExactMeasured from the rendered content
Long paragraph at a page edgePushed whole to the next pageSplitSplit between lines
Large documentsEntire document in the DOMPaged by the serverOnly the pages near the viewport
Text after renderingSelectableSelectable if the converter kept a text layerSelectable, searchable, addressable
Style cascadeFlattened during parsingExactSix layers resolved on demand
Runtime dependenciesJSZipAn entire serviceNone
Cost per documentZeroCompute, storage, licenceZero
Where a server still wins
If you need a legally identical reproduction for print or signature, convert on a server — that is a different product and we will tell you so. If you need a person to read, search and trust a document, that is this one.
How it works

Three stages that do not know about each other

Which is why changing the zoom re-runs only layout, and scrolling re-runs only virtualisation.

01

Render

The model becomes DOM. Formatting is emitted as shared CSS classes, never inline styles — a document with 200 000 runs produces about fifty rules instead of 200 000 style attributes.

02

Lay out

Blocks are measured in batches — one forced layout per batch, not per block — pages are broken, and the main thread is released between batches so the interface stays responsive and progress can be reported.

03

Virtualise

Every page is a fixed-size box from the start, so the scrollbar is correct before any content exists and mounting a page can never shift the scroll position.

Why a thousand pages open as fast as five

The techniques that matter, and the reason each one is there rather than the obvious alternative.

Streaming XML

Building a node tree for a 40 MB document.xml costs several hundred megabytes before any useful work starts. The pull parser builds the domain model directly and allocates nothing for branches it skips.

Interned properties

A document with a million runs contains a few dozen distinct character formats. Equal formatting becomes the same object, so the renderer keys its CSS classes on identity through a WeakMap.

Line boxes from the browser

Range.getClientRects() returns one rectangle per rendered line in a single call, and the split offset is then found by binary search — about eleven measurements per paragraph instead of one per character.

Bounded caches

Inflated ZIP entries are cached with an LRU budget, so a 500 MB document cannot be held in memory in full.

Integration

Five lines, whatever you build with

The React, Vue and Angular wrappers are thin adapters over one framework-independent class, so their behaviour is identical by construction rather than by convention.

import { DocumentViewer } from '@apertura/react';

export function DocumentPane({ file }: { file: File }) {
  // Word, Excel and PowerPoint — the format is detected from the bytes.
  return <DocumentViewer file={file} fit="width" />;
}
Architecture

Fifteen packages, pointing strictly downwards

Core knows nobody. Format packages know the container layer. Renderers know their format. The wrappers know only the facade. Take the whole thing or take one parser.

Said out loud

What this is not

  • This is not pixel-perfect Word, and it never will be. The browser lays out text, and its line breaking is not Word’s.
  • What is achievable — and what is measured — is that the content is all there, in the right order, correctly styled, with nothing missing, overlapping or garbled.
  • If your requirement is a legally identical reproduction for print or signature, convert to PDF on a server. That is a different product and we will say so.
  • If your requirement is that a person can read the document, search it, select from it and trust what they see, that is this one.
How fidelity is measuredThe full list of known limitations
Roadmap

A viewer today, a document engine next

The parsers already produce a model that runs unchanged in Node. Everything below is that model with a different emitter — not a second implementation to keep in agreement with the first.

Content extractionThe same parse, emitted as Markdown or structured text instead of DOM.Soon
Citation anchorsHighlight the exact place in the original document that an answer came from.Soon
Legacy binary formats.doc, .xls and .ppt — the formats nothing in the browser reads.Planned
The whole roadmap

Try it on your ugliest document

That is what the free tier is for, and it has no features removed. If something renders wrong, send it — it joins the corpus, which means it stays fixed.

  • Free for personal projects, open source, evaluation and companies under $250k a year.
  • Perpetual licences: what you paid for keeps working forever.
  • No network requests, no telemetry, works in an air-gapped network.