Performance vs AG Grid
Head-to-head scroll benchmarks against AG Grid Community, generated from the benchmark's latest run.
This page is our scoreboard against AG Grid. Everything on it comes from the latest run of the benchmark in
packages/bench: the numbers, the spread between rounds, the trend, and the list of problems the comparison
turned up. When the grid gets faster, re-running the benchmark updates this page. When AG Grid wins a
scenario, it says so.
What these numbers are, and are not
Both grids run in the same Chrome, with the same data, driven by real mouse-wheel input. We measure main-thread work (script, layout, style), input handling, frame intervals and blank area. The benchmark does not measure GPU rasterization, compositor-only checkerboarding, memory, or anything other than scrolling. Absolute milliseconds depend on the machine; compare the ratio between the grids, not the raw numbers across machines.
Scoreboard
A ratio below 1.00× means Wit Grid did less main-thread work than AG Grid for the same scroll. The verdict uses the whole spread of paired rounds, so a scenario is only called a win when every round agrees.
Scenarios
Each grid runs with its own defaults: we do not tune either one for the benchmark. The custom-renderer scenario
uses the closest equivalent in each grid: a DOM renderer with mount/update in Wit Grid, and a cell renderer
component with refresh() in AG Grid.
Vertical scroll, plain text
100,000 rows x 50 text columns; 150 wheel events of 360px down, then back up.
| Metric | Wit Grid | AG Grid | Better |
|---|---|---|---|
| Main-thread work | 1,597 ms | 2,864 ms | Wit Grid |
| Script | 475 ms | 1,058 ms | Wit Grid |
| Layout | 199 ms | 241 ms | Wit Grid |
| Style recalculation | 51 ms | 294 ms | Wit Grid |
| Input handling / event | 18.9 ms | 39.0 ms | Wit Grid |
| Frame time, p99 | 17.6 ms | 25.4 ms | Wit Grid |
| Dropped frames | 0.0% | 0.2% | Wit Grid |
| Blank samples | 0.0% | 0.0% | Even |
| Renderer mounts / updates | 0 / 0 | 0 / 0 | Context |
Vertical scroll, custom DOM renderers
Same grid with 10 columns drawn by a custom renderer (a bar and a label); each grid uses its own default renderer path.
| Metric | Wit Grid | AG Grid | Better |
|---|---|---|---|
| Main-thread work | 2,692 ms | 3,778 ms | Wit Grid |
| Script | 674 ms | 1,230 ms | Wit Grid |
| Layout | 316 ms | 319 ms | Even |
| Style recalculation | 134 ms | 441 ms | Wit Grid |
| Input handling / event | 31.7 ms | 58.6 ms | Wit Grid |
| Frame time, p99 | 25.7 ms | 25.6 ms | Even |
| Dropped frames | 0.0% | 0.0% | Even |
| Blank samples | 0.0% | 0.0% | Even |
| Renderer mounts / updates | 370 / 14,054 | 12,990 / 0 | Context |
Horizontal scroll, 200 columns
100,000 rows x 200 text columns; 150 wheel events of 300px right, then back left.
| Metric | Wit Grid | AG Grid | Better |
|---|---|---|---|
| Main-thread work | 1,668 ms | 2,632 ms | Wit Grid |
| Script | 667 ms | 992 ms | Wit Grid |
| Layout | 144 ms | 276 ms | Wit Grid |
| Style recalculation | 96 ms | 183 ms | Wit Grid |
| Input handling / event | 19.0 ms | 37.5 ms | Wit Grid |
| Frame time, p99 | 17.4 ms | 25.4 ms | Wit Grid |
| Dropped frames | 0.0% | 4.7% | Wit Grid |
| Blank samples | 0.0% | 0.0% | Even |
| Renderer mounts / updates | 0 / 0 | 0 / 0 | Context |
Very fast fling
90 wheel events of 2,400px, close to dragging the scrollbar; stresses blank area.
| Metric | Wit Grid | AG Grid | Better |
|---|---|---|---|
| Main-thread work | 1,891 ms | 4,084 ms | Wit Grid |
| Script | 585 ms | 1,823 ms | Wit Grid |
| Layout | 391 ms | 425 ms | Wit Grid |
| Style recalculation | 67 ms | 456 ms | Wit Grid |
| Input handling / event | 23.5 ms | 67.6 ms | Wit Grid |
| Frame time, p99 | 25.5 ms | 41.2 ms | Wit Grid |
| Dropped frames | 8.4% | 19.1% | Wit Grid |
| Blank samples | 0.0% | 0.0% | Even |
| Renderer mounts / updates | 0 / 0 | 0 / 0 | Context |
What the comparison found
Every issue the head-to-head surfaced, fixed or not. A problem stays on this list after it is fixed, so the history of what we learned from AG Grid stays visible.
Wit Grid spent more time in layout than AG Grid on vertical scroll (about 1.5x on the very fast fling). Chrome's layout trace located the cause: both grids render the same rows and cells and neither forces a layout, but every Wit text cell carried two extra layout objects: a content wrapper, and an anonymous flex box inside it (which also stopped long values from ending in an ellipsis).
Fixed for plain text and the fast fling. Cell text is a plain block, and cells of columns without a custom renderer hold their text directly, as AG Grid's do. Over a whole run Wit now dirties fewer layout objects than AG Grid on every vertical scenario (fast fling 87,480 vs 97,740; plain text 38,100 vs 42,300), and layout time is lower on both. Columns with custom DOM renderers are tracked separately below.
With 10 columns drawn by a custom DOM renderer, Wit Grid's layout time was about 10% above AG Grid's in consecutive runs (197 vs 178 ms, 274 vs 243 ms). A custom-renderer cell was three nested flex boxes (cell, portal host, renderer container) where AG Grid's cell holds the renderer directly.
Fixed to parity. The portal host no longer has a box of its own (display: contents), so the renderer container lays out directly in the cell. Layout time is now even with AG Grid within run-to-run noise (318 vs 324 ms and 324 vs 310 ms in two runs), and total main-thread work fell from 0.79x to about 0.70x of AG Grid's.
On the 200-column horizontal scroll Wit Grid spent more time in style recalculation than AG Grid (150 vs 108 ms). Chrome's invalidation tracking showed why: the grid ended and restarted its scroll session between wheel ticks, and each restart toggled a class on the grid container that restyled every row; separately, every visible header's sort indicator was rewritten on every frame.
Fixed. A scroll session now survives a stream of discrete scroll steps, and header indicators are written only when they change: over a run, container toggles fell from 280 to 2 and row restyles from 7,000 to 100. Style time is now below AG Grid's on every scenario. The benchmark sends instant wheel ticks 16 ms apart, which triggered the restart on every tick; wheels with smooth scrolling trigger it less, so real-world gains are smaller than the benchmark shows.
Cells entering during scroll showed a text stand-in, then flipped to the real renderer when scrolling settled: two writes per cell, and each flip changed the cell's content mode, layer visibility and key, forcing style recalculation. AG Grid updates its renderer in place with refresh().
DOM renderers now default to scrollPresentation 'update': an entering cell's update() runs in place within a per-frame budget and is final as drawn. With 10 custom-renderer columns Wit Grid went from about 1.23x AG Grid's main-thread work to 0.88x, ahead in every paired round.
0a67f8f9The DOM renderer manager treated a change of isScrolling or phase alone as a reason to call update(), so every mounted DOM renderer ran again when scrolling stopped, even when nothing it draws had changed.
Renderers are updated only when their value, row, column, or editing / focus / selection state changes. A renderer that wants scroll state opts in with capabilities.scrollState.
0a67f8f9Rows in the overscan band were written empty when no display snapshot existed, then filled when they scrolled in: a second write, a text node replacement and a content-mode change (a style recalculation) per cell.
Plain text columns fill those cells with their real value. On the plain-text scenario, content-mode changes went from 27,850 to 0 and style recalculation from 212 to 143 ms.
99d84650The custom and DOM renderer managers rewrote data-renderer-key and data-cell-key on every rebind, even when unchanged: about 44,000 DOM mutations each in one scroll.
Written only when the key changes.
99d84650The same scenario measured 1,534 ms in one run and 3,085 ms in another on a busy machine, with AG Grid moving just as much. Single-grid medians were not trustworthy.
Rounds are now paired and interleaved, and the page reports the per-round Wit/AG ratio with its full spread.
The first benchmark run reported 38% blank samples for Wit Grid on a scrollbar-drag-speed fling. The sampler was measuring before Wit Grid's own frame work had run; AG Grid renders inside the scroll event, so it was always measured after.
The benchmark now samples after each frame's work. Both grids show no main-thread blank area. Compositor-only checkerboarding is not measured by this benchmark.
6c5c8cb6Method
- Run on 2026-09-30 at commit
0cc7c90c, Wit Grid 1.4.1, AG Grid Community 36.2.0. - Chrome 154.0.8037.58 (headless) on Windows_NT 10.0.26200, AMD Ryzen 5 7535HS with Radeon Graphics (12 logical cores).
- Each round runs both grids back to back on the same scenario, alternating which goes first.
- Real mouse-wheel events through Chrome (playwright-core), 16ms apart; each grid uses its own defaults.
- Chrome main-thread counters (CDP Performance.getMetrics), frame intervals from rAF, long tasks, and blank area sampled after each frame.
Each round runs both grids back to back on the same scenario and alternates which one goes first, so machine drift, such as thermal throttling or background work, affects both equally. The ratio shown for a scenario is the median of the per-round ratios, with the lowest and highest round beside it.
Run it yourself
The benchmark drives your locally installed Chrome, so it needs no browser download.
pnpm bench:browserUseful flags:
--runs=9runs more paired rounds for a tighter spread.--only=vertical-dom-renderersruns a single scenario.--headedopens the browser so you can watch.--publishwrites the results into this page's data and adds a point to the trend.
Close other heavy applications first: a busy machine widens the spread between rounds, and the page shows that spread rather than hiding it.
Last updated on