</> Ultima

Benchmark

Performance comparison between Ultima and five close-by projects — Litedom, Alpine.js, Lit, Minze and VanJS — across four common reactive UI scenarios. Runs entirely in your browser; no number here was pre-computed.

Methodology. Each framework runs isolated in its own <iframe>, loaded from its official CDN (except Ultima, which uses the local code from this repository). Each operation is measured with performance.now() plus a double microtask flush afterward (to capture updates frameworks schedule via microtask, like Lit's batching or Alpine.nextTick) — the measurement deliberately does not include the browser's repaint: requestAnimationFrame is paused by the browser in background tabs, which would make running this in an automated way impossible. Each operation is repeated 5 times, and the reported value is the median of the 5. The list scenarios use 1000 rows; the counter/conditional/binding ones perform 1000 updates in sequence. This measures batched synchronous updates, not 1000 real user interactions — frameworks that batch updates into a microtask tend to do better in these specific scenarios, which reflects a real characteristic of their architecture, not an artificial advantage. VanJS has no keyed reconciliation (the framework has no such concept — the DOM nodes returned by van.tags are already the real elements); "swap" and "remove" are implemented by manipulating the nodes directly in that case, the way the documentation itself recommends. Minze documents neither keyed reconciliation nor native two-way binding; the list implementation follows the official performance guidance (avoid a reactive array, call rerender() manually) and the binding one is inferred (event + manual update). Litedom has had no commits since 2020 — the numbers are still real, but the project may not represent the current state of the art for its own approach.
Ready to run — takes about 1 to 2 minutes.

Full table

Metric

Sources