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.
This is not a screenshot. Open your own file and watch the network tab stay empty.
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 library | Backend conversion | Apertura | |
|---|---|---|---|
| Where it runs | Browser | A conversion service you run and scale | Browser |
| Where the document goes | Nowhere | Uploaded, converted, cached as a derivative | Nowhere |
| Page breaks | Word’s cached lastRenderedPageBreak hints | Exact | Measured from the rendered content |
| Long paragraph at a page edge | Pushed whole to the next page | Split | Split between lines |
| Large documents | Entire document in the DOM | Paged by the server | Only the pages near the viewport |
| Text after rendering | Selectable | Selectable if the converter kept a text layer | Selectable, searchable, addressable |
| Style cascade | Flattened during parsing | Exact | Six layers resolved on demand |
| Runtime dependencies | JSZip | An entire service | None |
| Cost per document | Zero | Compute, storage, licence | Zero |
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.
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.
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.
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.
One API, three file formats
Each format is a parser that knows nothing about the DOM and a renderer that knows nothing about the file. The facade picks the pair from the bytes.
Paginated the way Word paginates — by measuring, not by guessing.
Shows what the author saw, not what the file stores.
A fixed canvas, which is why it can get very close.
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" />;
}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.
Foundation
Knows nothing about any particular format.Formats
A parser that knows no DOM, and a renderer that knows no file.Integration
One dependency for an application that just needs to open a file.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.
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.
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.