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.

Vertical scroll, plain text
0.55×
Wit Grid main-thread work relative to AG Grid
Wit Grid faster in every round
5 paired rounds, 0.48× – 0.61×
Vertical scroll, custom DOM renderers
0.73×
Wit Grid main-thread work relative to AG Grid
Wit Grid faster in every round
5 paired rounds, 0.68× – 0.73×
Horizontal scroll, 200 columns
0.64×
Wit Grid main-thread work relative to AG Grid
Wit Grid faster in every round
5 paired rounds, 0.61× – 0.68×
Very fast fling
0.47×
Wit Grid main-thread work relative to AG Grid
Wit Grid faster in every round
5 paired rounds, 0.44× – 0.53×

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.

Wit Grid1,597 ms
AG Grid2,864 ms
Script Layout Style Other
MetricWit GridAG GridBetter
Main-thread work1,597 ms2,864 msWit Grid
Script475 ms1,058 msWit Grid
Layout199 ms241 msWit Grid
Style recalculation51 ms294 msWit Grid
Input handling / event18.9 ms39.0 msWit Grid
Frame time, p9917.6 ms25.4 msWit Grid
Dropped frames0.0%0.2%Wit Grid
Blank samples0.0%0.0%Even
Renderer mounts / updates0 / 00 / 0Context
Wit/AG main-thread ratio across published runs

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.

Wit Grid2,692 ms
AG Grid3,778 ms
Script Layout Style Other
MetricWit GridAG GridBetter
Main-thread work2,692 ms3,778 msWit Grid
Script674 ms1,230 msWit Grid
Layout316 ms319 msEven
Style recalculation134 ms441 msWit Grid
Input handling / event31.7 ms58.6 msWit Grid
Frame time, p9925.7 ms25.6 msEven
Dropped frames0.0%0.0%Even
Blank samples0.0%0.0%Even
Renderer mounts / updates370 / 14,05412,990 / 0Context
Wit/AG main-thread ratio across published runs

Horizontal scroll, 200 columns

100,000 rows x 200 text columns; 150 wheel events of 300px right, then back left.

Wit Grid1,668 ms
AG Grid2,632 ms
Script Layout Style Other
MetricWit GridAG GridBetter
Main-thread work1,668 ms2,632 msWit Grid
Script667 ms992 msWit Grid
Layout144 ms276 msWit Grid
Style recalculation96 ms183 msWit Grid
Input handling / event19.0 ms37.5 msWit Grid
Frame time, p9917.4 ms25.4 msWit Grid
Dropped frames0.0%4.7%Wit Grid
Blank samples0.0%0.0%Even
Renderer mounts / updates0 / 00 / 0Context
Wit/AG main-thread ratio across published runs

Very fast fling

90 wheel events of 2,400px, close to dragging the scrollbar; stresses blank area.

Wit Grid1,891 ms
AG Grid4,084 ms
Script Layout Style Other
MetricWit GridAG GridBetter
Main-thread work1,891 ms4,084 msWit Grid
Script585 ms1,823 msWit Grid
Layout391 ms425 msWit Grid
Style recalculation67 ms456 msWit Grid
Input handling / event23.5 ms67.6 msWit Grid
Frame time, p9925.5 ms41.2 msWit Grid
Dropped frames8.4%19.1%Wit Grid
Blank samples0.0%0.0%Even
Renderer mounts / updates0 / 00 / 0Context
Wit/AG main-thread ratio across published runs

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.

Fixed
More layout work than AG Grid on vertical scroll

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.

Fixed
Slightly more layout work than AG Grid with custom DOM renderers

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.

Fixed
More style recalculation than AG Grid on horizontal scroll

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.

Fixed
Custom DOM renderer columns did more work than AG Grid

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.

0a67f8f9
Fixed
Scroll start and end re-ran every DOM renderer

The 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.

0a67f8f9
Fixed
Cells just outside the viewport were cleared, then refilled

Rows 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.

99d84650
Fixed
Renderer key attributes rewritten on every bind

The 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.

99d84650
Fixed
Results varied widely between runs

The 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.

Not an issue
Blank rows on a very fast fling

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.

6c5c8cb6

Method

  • 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:browser

Useful flags:

  • --runs=9 runs more paired rounds for a tighter spread.
  • --only=vertical-dom-renderers runs a single scenario.
  • --headed opens the browser so you can watch.
  • --publish writes 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.

Edit on GitHub

Last updated on

On this page