Architecture Overview
Data flow
JSX → createElement() → JS tree → serde-wasm-bindgen → Rust DocumentTree
→ docxio-ooxml XML → docxio-core ZIP → Uint8Array
┌──────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ JSX / TSX │ ──► │ JS Object Tree │ ──► │ Rust via WASM │
│ components │ │ (DocumentNode) │ │ docxio-core │
└───────────── ─┘ └──────────────────┘ └────────┬────────┘
│
┌────────▼────────┐
│ OOXML XML + │
│ ZIP packaging │
│ → Uint8Array │
└─────────────────┘
Step-by-step
- JSX authoring -- Developers write documents using JSX syntax with
<document>,<paragraph>,<run>, etc. - createElement -- Bun/tsc transpiles JSX to
docxio/jsx-runtimecalls, producing a plain JS object tree. - Tree transform -- The
transformTreefunction normalizes the JS tree for the Rust side. - FFI boundary -- The tree crosses into WASM via a single
serde-wasm-bindgencall (no JSON.stringify overhead). - XML generation --
docxio-ooxmlgenerates OOXML XML parts (document.xml, styles.xml, numbering.xml, etc.) using thequick-xmlwriter API. - ZIP packaging --
docxio-coreassembles all XML parts plus media files into a.docxZIP archive. - Return -- The ZIP bytes are returned as a
Uint8Arrayto JavaScript.
Package structure
TypeScript packages
| Package | Size | Role |
|---|---|---|
docxio | 15 KB JS + WASM | Core: JSX runtime, transformer, render functions |
@docxio/html | ~5 KB | HTML and Markdown export |
@docxio/zip | ~8 KB | JS-side ZIP assembly for minimal backend |
@docxio/bun | ~2 KB | Bun build plugin |
@docxio/node | native | napi-rs server binding |
@docxio/wasm-minimal | 441 KB | Minimal WASM binary |
Rust crates
docxio-types → Shared document model (serde + tsify)
↑
docxio-ooxml → OOXML XML generation (quick-xml)
↑
docxio-core → Orchestration: tree → XML → ZIP
↑
├── docxio-wasm → Full WASM binding (920 KB)
├── docxio-wasm-minimal → Minimal WASM binding (441 KB)
├── docxio-napi → Native Node.js binding (napi-rs)
├── docxio-cli → CLI tool (clap)
├── docxio-pdf → PDF export (planned)
├── docxio-bench → Benchmarks (criterion)
└── docxio-fuzz → Fuzz testing
See Rust Crates for detailed crate responsibilities.
Design principles
- Single FFI call -- The entire document tree crosses the WASM boundary once, avoiding per-element overhead.
- No React dependency -- The JSX factory is a simple
createElementfunction. Documents are static trees, not reactive components. - Layered crates -- Each Rust crate has a single responsibility.
docxio-typeshas no dependencies on XML or ZIP. - Modular WASM -- The minimal build excludes ZIP, template, and validation code for smaller browser bundles.
- XML safety -- All user text is escaped via
quick-xml. No string concatenation for XML generation.