Performance Facts
Real numbers from one local machine, measured against real interactions — DataGrid rendering a large dataset, the resize/reorder hot paths, a toast burst, and WASM boot time. Same honesty standard as Bundle Facts: the exact methodology per metric, an honest reproduce section, and the caveats we hit along the way.
Column reorder moves cost 0.0000 ms of JS per move event
Median of 3,000 synthetic pointer moves against a loaded grid, measured 2026-07-12 (4.2.0+main) on one local Windows machine — see "How each metric was measured" below. (Resize's own number is enqueue-only handler cost, not directly comparable — see below.)
The measured numbers
Median of 5 independent runs per metric, fresh browser page per run. Full per-run data lives in
scripts/perf/results/*.json.
DataGrid initial render
10,000 rows, click-to-first-row, virtualized
Virtualized scroll
10,000 rows, 1s scroll sweep
Sort 10k rows
Click header → aria-sort flips + re-render
Filter 10k rows
Toolbar search input → body re-renders
Column resize (enqueue-only)
Handler cost per move; excludes the deferred rAF width write
Column reorder
Per pointer-move event, live sibling-shift
Toast burst settle
5 toasts (see MaxToasts crash caveat)
WASM boot-to-interactive
Trigger → MainLayout first interactive render
How each metric was measured
All four scripts drive
docs/Lumeo.Docs/Pages/E2E/PerfBench.razor
(a noindex, nav-less harness page — same pattern as the existing
P0Harness.razor) or the docs home, with Playwright.
Caveats
This is not a lab. Read these before quoting a number.
Single local Windows dev machine, not a CI runner or a lab bench. Absolute numbers will differ on your hardware — the relative shape (hot paths sub-millisecond, bulk operations low-hundreds-of-ms) is the more portable takeaway.
Chromium only, via Playwright headless. No Firefox/WebKit/mobile numbers here.
Served by dotnet run's Development dev-server, not a published, CDN-hosted production build — real production latency will differ (mostly for network-bound metrics; the in-page timings above don't depend on transport).
This build is non-AOT-compiled (interpreter tier). AOT-published apps trade a slower build for a materially faster runtime — the DataGrid bulk-operation numbers above are pessimistic relative to what an AOT-published app would show.
DataGrid's automated dataset is 10,000 rows, not the 100,000 this page's title once implied — see "Why 10k rows, not 100k" above for the measured 100k OOM and the 75k/~60s render finding.
The toast burst is 5 toasts, not 100 — see "A real bug, disclosed rather than hidden" above.
The column-resize figure (0.0033 ms) is enqueue-only handler cost and excludes the deferred rAF DOM write a real drag pays — it is not directly comparable to reorder's figure, which includes its (synchronous) DOM write. See "Column resize / reorder" above.
The WASM boot number is measured against the docs app's DEFAULT (auto-calculated ~108 MB) heap, not the 512 MB perf heap the DataGrid/toast benchmarks below use — see "How to reproduce" for why boot needs a separate server session.
How to reproduce
Every script and its full per-run JSON output lives in the repo —
scripts/perf/. We believe the numbers; check them yourself.
# 1a. Boot cost — DEFAULT heap (what real visitors get) export DOTNET_ROLL_FORWARD=Major cd docs/Lumeo.Docs dotnet run --arch x64 -c Release --urls http://localhost:5287 # in another shell cd scripts/perf npm install node wasm-boot.mjs # stop the server, then: # 1b. DataGrid / hot-path / toast benchmarks — need the 512 MB perf heap cd docs/Lumeo.Docs dotnet run --arch x64 -c Release --urls http://localhost:5287 -p:LumeoPerfHeap=true # in another shell cd scripts/perf node datagrid-100k.mjs node datagrid-hotpaths.mjs node toast-burst.mjs
Two server sessions on purpose — WasmInitialHeapSize
is a publish-time property for the whole app, not scoped per route, so an accurate boot number needs a
session without the perf heap. See scripts/perf/README.md.
See it for yourself
Run the scripts, read the JSON, poke at the harness page. We'd rather you distrust a number and check it than take our word for it.