Measured in #451 (status/milestones/2026-08-16-s2-mount-and-interaction.json), on a quiet machine, like-for-like on wrapping and pinning.
Interaction latency in ticks of a 120Hz frame, n=7 per cell:
| script |
tanstack |
pretable |
| sort |
2–5t (17–42ms) |
6–8t (50–67ms) |
| filter-metadata |
2–4t (17–33ms) |
5–7t (42–58ms) |
| filter-text |
1–4t (8–33ms) |
6–7t (50–58ms) |
pretable centres on 7 ticks (58ms), TanStack on 3–4 ticks (25–33ms). Consistent across three structurally different interactions and 21 repeats each, with barely overlapping distributions — so this is not the sampling artifact that invalidated the scroll medians in #450.
Where it probably is not
Rendering. pretable settles with the fewest DOM nodes of the four adapters:
|
pretable |
tanstack |
ag-grid |
mui |
| rendered rows at settle |
7–8 |
6–7 |
16–18 |
11 |
| DOM nodes at settle |
100–107 |
294–335 |
474–506 |
1180 |
Doing the least DOM work and still losing to two of the three points at the row model — filtering and sorting 3,000 rows — rather than at paint.
What would settle it
A CPU profile sliced to the interaction window, which nobody has taken:
PLAYWRIGHT_PERF_TRACE=1 pnpm bench:matrix --adapters=pretable --scenarios=S2 --scripts=sort --scale=hypothesis --repeats=1
then node scripts/analyze-cdp.mjs <trace> <bundle>.js.map --window=interaction. Note the window flag: the full-trace view is dominated by mount work that does not count against interaction_latency_ms.
Worth knowing before optimising: #388 filed a scroll cost as an accuracy-vs-speed trade and the profile showed it was neither — it was redundant grapheme segmentation. The frame this issue names may likewise not be where it looks.
Caveat
interaction_latency_ms is frame-quantised, so pretable's 7 ticks and TanStack's 4 differ by three frames, not by "33ms" precisely. The gap is real and reproducible; the exact millisecond figure is not the thing to optimise against.
Measured in #451 (
status/milestones/2026-08-16-s2-mount-and-interaction.json), on a quiet machine, like-for-like on wrapping and pinning.Interaction latency in ticks of a 120Hz frame, n=7 per cell:
pretable centres on 7 ticks (58ms), TanStack on 3–4 ticks (25–33ms). Consistent across three structurally different interactions and 21 repeats each, with barely overlapping distributions — so this is not the sampling artifact that invalidated the scroll medians in #450.
Where it probably is not
Rendering. pretable settles with the fewest DOM nodes of the four adapters:
Doing the least DOM work and still losing to two of the three points at the row model — filtering and sorting 3,000 rows — rather than at paint.
What would settle it
A CPU profile sliced to the interaction window, which nobody has taken:
then
node scripts/analyze-cdp.mjs <trace> <bundle>.js.map --window=interaction. Note the window flag: the full-trace view is dominated by mount work that does not count againstinteraction_latency_ms.Worth knowing before optimising: #388 filed a scroll cost as an accuracy-vs-speed trade and the profile showed it was neither — it was redundant grapheme segmentation. The frame this issue names may likewise not be where it looks.
Caveat
interaction_latency_msis frame-quantised, so pretable's 7 ticks and TanStack's 4 differ by three frames, not by "33ms" precisely. The gap is real and reproducible; the exact millisecond figure is not the thing to optimise against.