Skip to main content

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

  1. JSX authoring -- Developers write documents using JSX syntax with <document>, <paragraph>, <run>, etc.
  2. createElement -- Bun/tsc transpiles JSX to docxio/jsx-runtime calls, producing a plain JS object tree.
  3. Tree transform -- The transformTree function normalizes the JS tree for the Rust side.
  4. FFI boundary -- The tree crosses into WASM via a single serde-wasm-bindgen call (no JSON.stringify overhead).
  5. XML generation -- docxio-ooxml generates OOXML XML parts (document.xml, styles.xml, numbering.xml, etc.) using the quick-xml writer API.
  6. ZIP packaging -- docxio-core assembles all XML parts plus media files into a .docx ZIP archive.
  7. Return -- The ZIP bytes are returned as a Uint8Array to JavaScript.

Package structure

TypeScript packages

PackageSizeRole
docxio15 KB JS + WASMCore: JSX runtime, transformer, render functions
@docxio/html~5 KBHTML and Markdown export
@docxio/zip~8 KBJS-side ZIP assembly for minimal backend
@docxio/bun~2 KBBun build plugin
@docxio/nodenativenapi-rs server binding
@docxio/wasm-minimal441 KBMinimal 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 createElement function. Documents are static trees, not reactive components.
  • Layered crates -- Each Rust crate has a single responsibility. docxio-types has 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.