Architecture
High-level flow: React components and hooks → JS adapter (column registry, data ingestion, memory bridge) → WASM core (columnar store, Taffy layout, virtual scroll) → canvas renderer.
System overview
Data flow
Data ingestion (on data change)
Object[]arrives from user (e.g. API response).- JS DataIngestor classifies columns by type (float64 / bool / string).
- Numeric/bool columns →
Float64Array→ WASM memory copy (no serde). - String columns → JS StringTable (display) + intern IDs → WASM (sort/filter).
- ColumnarStore stores type-specific arrays and marks the view dirty.
Render cycle (per frame)
- React reconciles → column configs collected via ColumnRegistry.
- Single WASM call:
engine.updateViewportColumnar(scrollTop, viewport, columns).- ColumnarStore rebuilds view indices (filter → sort on u32 indices).
- VirtualScroll computes visible row range.
- Taffy computes flexbox layout for visible cells.
- Writes to flat Float32Array layout buffer (stride 8 per cell).
- JS reads layout buffer via zero-copy MemoryBridge (Float32Array view).
- Canvas renderer draws each cell using layout buffer + StringTable.
Scroll cycle (hot path)
React is not involved in the scroll path so the engine can keep 60fps with large data.
Edit cycle
Editors are DOM overlays (canvas cannot host native inputs). Only the active cell uses DOM; the rest stays on canvas.
Taffy integration
Taffy is a Rust flexbox/grid layout engine. We use it to compute cell positions.
Layout is computed only for visible rows (virtual scroll window), so Taffy runs on a small set of nodes each frame.
Module boundaries
- crates/core — Pure Rust (no WASM): ColumnarStore, LayoutEngine, sort/filter, virtual slice. Testable with
cargo test. - crates/wasm — WASM bindings: TypedArray ingestion,
updateViewportColumnar, layout buffer access. Thin layer over core. - packages/react-wasm-table (JS) — ColumnRegistry, DataIngestor, StringTable, MemoryBridge, EventManager, EditorManager, CanvasRenderer, React components and hooks.