Benchmarking Infinite Table, MUI X Data Grid, AG Grid and TanStack Table with a million rows

By raduยท

View as Markdown
With release 10.1.4 we improved Infinite Table rendering to make it possible to render huge amounts of data in your favorite React DataGrid. Previously, we were capped at about 400k records, but now you can go much higher, so we wanted to test 1 million records and see if performance stays sharp with this many records. So we put it to work and compared it against three other React DataGrids: MUI X Data Grid, AG Grid and TanStack Table.
We build Infinite Table, so you should read any benchmark we publish with healthy suspicion. That's exactly why we made this one open: the code, the raw numbers, and the exact commands are in the datagrid-performance-comparison repository, and you can run the whole thing on your own machine in about an hour.
The goal was not to declare a winner. It was to measure where each grid spends its time - and Infinite Table is not ahead everywhere.
The short version:
  • Sorting: Infinite Table is the fastest of the four in every case we measured, by 1.6ร— to 3.3ร—.
  • Memory: Infinite Table keeps the least - 91 MB for a million rows, against 150 MB for MUI X Data Grid, 213 MB for AG Grid and 287 MB for TanStack Table.
  • Mounting: with a million rows Infinite Table is the first to show them. With 300k rows TanStack Table is 6% faster than Infinite Table, which is faster than the other two.
  • Scrolling: TanStack Table, rendered with TanStack Virtual, does the least work while scrolling vertically and is the fastest to jump to a far-away row - with a much simpler page than a full grid. Infinite Table does the least work scrolling horizontally at 300k rows, and never shows blank rows; it also had the longest single frame while scrolling vertically.
  • A million rows: only Infinite Table and AG Grid can scroll to the last one. MUI X Data Grid and TanStack Table stop at row 838,860.
At a glance - 1,000,000 rows
median of 7 runs ยท lower is better
Mount: first rows painted
MUI X Data Grid745 ms745 msMUI X Data GridMount: first rows painted8% lower than AG Grid13% higher than TanStack Table32% higher than Infinite Table
AG Grid808 ms808 msAG GridMount: first rows painted8% higher than MUI X Data Grid23% higher than TanStack Table43% higher than Infinite Table
TanStack Table659 ms659 msTanStack TableMount: first rows painted12% lower than MUI X Data Grid18% lower than AG Grid17% higher than Infinite Table
Infinite Table565 ms565 msInfinite TableMount: first rows painted24% lower than MUI X Data Grid30% lower than AG Grid14% lower than TanStack Table
Sort a string column
MUI X Data Grid5,466 ms5,466 msMUI X Data GridSort a string column34% higher than AG Grid10% lower than TanStack Table158% higher than Infinite Table
AG Grid4,067 ms4,067 msAG GridSort a string column26% lower than MUI X Data Grid33% lower than TanStack Table92% higher than Infinite Table
TanStack Table6,083 ms6,083 msTanStack TableSort a string column11% higher than MUI X Data Grid50% higher than AG Grid187% higher than Infinite Table
Infinite Table2,120 ms2,120 msInfinite TableSort a string column61% lower than MUI X Data Grid48% lower than AG Grid65% lower than TanStack Table
Sort a number column
MUI X Data Grid2,596 ms2,596 msMUI X Data GridSort a number column21% lower than AG Grid42% lower than TanStack Table89% higher than Infinite Table
AG Grid3,306 ms3,306 msAG GridSort a number column27% higher than MUI X Data Grid27% lower than TanStack Table141% higher than Infinite Table
TanStack Table4,510 ms4,510 msTanStack TableSort a number column74% higher than MUI X Data Grid36% higher than AG Grid228% higher than Infinite Table
Infinite Table1,374 ms1,374 msInfinite TableSort a number column47% lower than MUI X Data Grid58% lower than AG Grid70% lower than TanStack Table
View as table
At a glance - 1,000,000 rowsMUI X Data GridAG GridTanStack TableInfinite Table
Mount: first rows painted745 ms808 ms659 ms565 ms
Sort a string column5,466 ms4,067 ms6,083 ms2,120 ms
Sort a number column2,596 ms3,306 ms4,510 ms1,374 ms

The four grids#

Infinite Table and AG Grid are full data grids that show all the rows in one scrollable view. The other two need a few words.

MUI X Data Grid: all the rows on one page#

We used the free, MIT-licensed DataGrid. It always paginates, and by default a page has 100 rows. To make it show what the other grids show, we gave it a page size of -1, the value its documentation gives for "all results". Every row is then on one page, which the grid virtualizes and scrolls through like the others do. Its pagination footer stays, and reads "1โ€“1,000,000 of 1,000,000".

TanStack Table: headless, with our markup#

TanStack Table has the row model and the sorting, and renders nothing. What we measured is what its documentation recommends for large tables: TanStack Table, TanStack Virtual for the rows and the columns, and plain <table> markup. The page follows the official virtualized-columns example, with the same overscan.
That page is much less than a grid. It has no keyboard navigation, focus, cell or row selection, column menus, resizing, reordering or pinned columns, and it renders 352 DOM elements where the other three render 529 to 637. Doing less on every frame is the point of a headless library, and it shows in the scrolling numbers.

The setup#

Every grid renders the same data, in the same browser, with the same interactions. We kept each grid on its defaults and only set what was needed to make the layouts identical.
  • Data: 300,000 and 1,000,000 rows ร— 20 columns, generated deterministically. Every grid imports the same module, and the harness verifies a checksum, and that all the grids show the same text, before every run.
  • Configuration: same row height, header height, and column widths, and all the rows in one scrollable view. No other tuning, except:
    • MUI X Data Grid has the page size of -1 described above, and shows numbers as they are - by default it formats them for the locale ("100,000.13").
    • AG Grid uses the Quartz theme with animateRows={false} (the other grids don't animate rows on sort, and the animation would make "sorted rows visible" ambiguous) and a getRowId (the other grids have a row id too).
    • TanStack Table has sortDescFirst: false: by default its first click on a number column sorts descending, and in the other grids it sorts ascending. Its rows have a fixed height, so nothing is measured while scrolling.
  • Versions: all four are installed from npm: Infinite Table 10.1.5 (@infinite-table/infinite-react), MUI X Data Grid 9.17.0 (@mui/x-data-grid), AG Grid React Community 36.3.0 (ag-grid-react and ag-grid-community) and TanStack Table 9.3.1 (@tanstack/react-table) with TanStack Virtual 3.14.26. Each page loads only its own grid.
  • License keys: none. Infinite Table works the same without one, but shows a "Powered by Infinite Table" footer, and MUI X Data Grid has its pagination footer. The benchmark makes the boxes taller until every grid scrolls a body of the same height, and renders the same number of rows.
  • Builds: production builds only - the benchmark refuses to run against a dev server.
  • Browser: Chrome 136, headless, sandboxed like the Chrome your users run, en-US locale, no CPU throttling, 1400ร—1000 viewport.
  • Machine: a GitHub Actions hosted runner (Ubuntu 24.04, 4 cores of an AMD EPYC 9V74, no GPU). It is slower than a recent laptop, and anyone can start the same run on the same kind of machine.
  • Runs: 7 measured runs per grid per scenario, plus 1 discarded warmup. The grids are interleaved - each run goes through all of them, in an order that changes from one run to the next - and every run uses a fresh browser context.

How we measure#

Latency is read from Chrome performance traces, the same way js-framework-benchmark does it: from the input event (pointerdown, setState, or setting scrollTop) to the end of the first frame Chrome commits where the DOM shows the expected content - the right first and last visible rows and columns, at the right place.
Scrolling uses Chrome's real input pipeline (Input.synthesizeScrollGesture), so every grid gets exactly the same wheel gesture, with threaded scrolling enabled. Frame outcomes come from Chrome's compositor: a frame is either fully presented (it includes the grid's latest main-thread update), presented without the main-thread update (the compositor scrolled what it already had), or dropped.
For every metric we report medians. Infinite Table is compared with each of the other grids, with a 95% bootstrap confidence interval of the difference; we only call a difference when that interval excludes "no difference".
Every chart below has a "View as table" link with its exact numbers, and the full report has every metric of every scenario.

Mounting the grid#

Time from setState with all rows already in memory to the first frame showing the full viewport, and until the grid stops changing the DOM.
Mount time
median of 7 runs ยท lower is better
First rows painted, 300k rows
MUI X Data Grid275 ms275 msMUI X Data GridFirst rows painted, 300k rows15% lower than AG Grid19% higher than TanStack Table12% higher than Infinite Table
AG Grid323 ms323 msAG GridFirst rows painted, 300k rows17% higher than MUI X Data Grid39% higher than TanStack Table31% higher than Infinite Table
TanStack Table232 ms232 msTanStack TableFirst rows painted, 300k rows16% lower than MUI X Data Grid28% lower than AG Grid6% lower than Infinite Table
Infinite Table246 ms246 msInfinite TableFirst rows painted, 300k rows11% lower than MUI X Data Grid24% lower than AG Grid6% higher than TanStack Table
Settled (no more DOM changes), 300k rows
MUI X Data Grid275 ms275 msMUI X Data GridSettled (no more DOM changes), 300k rows35% lower than AG Grid19% higher than TanStack Table12% higher than Infinite Table
AG Grid425 ms425 msAG GridSettled (no more DOM changes), 300k rows55% higher than MUI X Data Grid83% higher than TanStack Table73% higher than Infinite Table
TanStack Table232 ms232 msTanStack TableSettled (no more DOM changes), 300k rows16% lower than MUI X Data Grid45% lower than AG Grid6% lower than Infinite Table
Infinite Table246 ms246 msInfinite TableSettled (no more DOM changes), 300k rows11% lower than MUI X Data Grid42% lower than AG Grid6% higher than TanStack Table
First rows painted, 1M rows
MUI X Data Grid745 ms745 msMUI X Data GridFirst rows painted, 1M rows8% lower than AG Grid13% higher than TanStack Table32% higher than Infinite Table
AG Grid808 ms808 msAG GridFirst rows painted, 1M rows8% higher than MUI X Data Grid23% higher than TanStack Table43% higher than Infinite Table
TanStack Table659 ms659 msTanStack TableFirst rows painted, 1M rows12% lower than MUI X Data Grid18% lower than AG Grid17% higher than Infinite Table
Infinite Table565 ms565 msInfinite TableFirst rows painted, 1M rows24% lower than MUI X Data Grid30% lower than AG Grid14% lower than TanStack Table
Settled, 1M rows
MUI X Data Grid745 ms745 msMUI X Data GridSettled, 1M rows18% lower than AG Grid13% higher than TanStack Table31% higher than Infinite Table
AG Grid911 ms911 msAG GridSettled, 1M rows22% higher than MUI X Data Grid38% higher than TanStack Table61% higher than Infinite Table
TanStack Table659 ms659 msTanStack TableSettled, 1M rows12% lower than MUI X Data Grid28% lower than AG Grid16% higher than Infinite Table
Infinite Table567 ms567 msInfinite TableSettled, 1M rows24% lower than MUI X Data Grid38% lower than AG Grid14% lower than TanStack Table
View as table
Mount timeMUI X Data GridAG GridTanStack TableInfinite Table
First rows painted, 300k rows275 ms323 ms232 ms246 ms
Settled (no more DOM changes), 300k rows275 ms425 ms232 ms246 ms
First rows painted, 1M rows745 ms808 ms659 ms565 ms
Settled, 1M rows745 ms911 ms659 ms567 ms
With a million rows Infinite Table paints the first rows sooner than the other three: 14% sooner than TanStack Table, 24% sooner than MUI X Data Grid and 30% sooner than AG Grid. With 300k rows TanStack Table is the fastest, 6% ahead of Infinite Table, which is 11% ahead of MUI X Data Grid and 24% ahead of AG Grid. AG Grid keeps changing the DOM for another 100 ms after its first paint; the other three are done when the rows are painted.
Memory: JavaScript heap retained by the grid
after garbage collection, median of 7 runs ยท lower is better
300k rows
MUI X Data Grid53.9 MB53.9 MBMUI X Data Grid300k rows20% lower than AG Grid38% lower than TanStack Table47% higher than Infinite Table
AG Grid67.4 MB67.4 MBAG Grid300k rows25% higher than MUI X Data Grid22% lower than TanStack Table84% higher than Infinite Table
TanStack Table86.6 MB86.6 MBTanStack Table300k rows61% higher than MUI X Data Grid28% higher than AG Grid137% higher than Infinite Table
Infinite Table36.6 MB36.6 MBInfinite Table300k rows32% lower than MUI X Data Grid46% lower than AG Grid58% lower than TanStack Table
1M rows
MUI X Data Grid150 MB150 MBMUI X Data Grid1M rows30% lower than AG Grid48% lower than TanStack Table65% higher than Infinite Table
AG Grid213 MB213 MBAG Grid1M rows42% higher than MUI X Data Grid26% lower than TanStack Table135% higher than Infinite Table
TanStack Table287 MB287 MBTanStack Table1M rows91% higher than MUI X Data Grid35% higher than AG Grid216% higher than Infinite Table
Infinite Table90.7 MB90.7 MBInfinite Table1M rows40% lower than MUI X Data Grid57% lower than AG Grid68% lower than TanStack Table
View as table
Memory: JavaScript heap retained by the gridMUI X Data GridAG GridTanStack TableInfinite Table
300k rows53.9 MB67.4 MB86.6 MB36.6 MB
1M rows150 MB213 MB287 MB90.7 MB
Infinite Table retains the least memory at both sizes. At 1M rows it keeps 40% less than MUI X Data Grid, 57% less than AG Grid and 68% less than TanStack Table, which creates a row object for every one of the million rows. The DOM is not where the difference comes from - the grids render a few hundred elements each - it is in the data structures each one builds over the rows.

Sorting#

Click a column header, measured until every visible row shows sorted, consistent data. We sorted a string column and a number column, ascending (the first, cold sort) and then descending.
Sorting 300,000 rows: click to sorted rows painted
median of 7 runs ยท lower is better
String column, ascending
MUI X Data Grid1,275 ms1,275 msMUI X Data GridString column, ascending32% higher than AG Grid6% lower than TanStack Table162% higher than Infinite Table
AG Grid969 ms969 msAG GridString column, ascending24% lower than MUI X Data Grid28% lower than TanStack Table99% higher than Infinite Table
TanStack Table1,354 ms1,354 msTanStack TableString column, ascending6% higher than MUI X Data Grid40% higher than AG Grid178% higher than Infinite Table
Infinite Table487 ms487 msInfinite TableString column, ascending62% lower than MUI X Data Grid50% lower than AG Grid64% lower than TanStack Table
String column, descending
MUI X Data Grid1,205 ms1,205 msMUI X Data GridString column, descending20% higher than AG Grid11% lower than TanStack Table111% higher than Infinite Table
AG Grid1,008 ms1,008 msAG GridString column, descending16% lower than MUI X Data Grid26% lower than TanStack Table77% higher than Infinite Table
TanStack Table1,354 ms1,354 msTanStack TableString column, descending12% higher than MUI X Data Grid34% higher than AG Grid137% higher than Infinite Table
Infinite Table571 ms571 msInfinite TableString column, descending53% lower than MUI X Data Grid43% lower than AG Grid58% lower than TanStack Table
Number column, ascending
MUI X Data Grid649 ms649 msMUI X Data GridNumber column, ascending26% lower than AG Grid40% lower than TanStack Table63% higher than Infinite Table
AG Grid879 ms879 msAG GridNumber column, ascending35% higher than MUI X Data Grid19% lower than TanStack Table121% higher than Infinite Table
TanStack Table1,083 ms1,083 msTanStack TableNumber column, ascending67% higher than MUI X Data Grid23% higher than AG Grid172% higher than Infinite Table
Infinite Table398 ms398 msInfinite TableNumber column, ascending39% lower than MUI X Data Grid55% lower than AG Grid63% lower than TanStack Table
Number column, descending
MUI X Data Grid886 ms886 msMUI X Data GridNumber column, descending3% higher than AG Grid13% lower than TanStack Table128% higher than Infinite Table
AG Grid861 ms861 msAG GridNumber column, descending3% lower than MUI X Data Grid16% lower than TanStack Table121% higher than Infinite Table
TanStack Table1,019 ms1,019 msTanStack TableNumber column, descending15% higher than MUI X Data Grid18% higher than AG Grid162% higher than Infinite Table
Infinite Table389 ms389 msInfinite TableNumber column, descending56% lower than MUI X Data Grid55% lower than AG Grid62% lower than TanStack Table
View as table
Sorting 300,000 rows: click to sorted rows paintedMUI X Data GridAG GridTanStack TableInfinite Table
String column, ascending1,275 ms969 ms1,354 ms487 ms
String column, descending1,205 ms1,008 ms1,354 ms571 ms
Number column, ascending649 ms879 ms1,083 ms398 ms
Number column, descending886 ms861 ms1,019 ms389 ms
Sorting 1,000,000 rows: click to sorted rows painted
median of 7 runs ยท lower is better
String column, ascending
MUI X Data Grid5,466 ms5,466 msMUI X Data GridString column, ascending34% higher than AG Grid10% lower than TanStack Table158% higher than Infinite Table
AG Grid4,067 ms4,067 msAG GridString column, ascending26% lower than MUI X Data Grid33% lower than TanStack Table92% higher than Infinite Table
TanStack Table6,083 ms6,083 msTanStack TableString column, ascending11% higher than MUI X Data Grid50% higher than AG Grid187% higher than Infinite Table
Infinite Table2,120 ms2,120 msInfinite TableString column, ascending61% lower than MUI X Data Grid48% lower than AG Grid65% lower than TanStack Table
String column, descending
MUI X Data Grid5,478 ms5,478 msMUI X Data GridString column, descending32% higher than AG Grid6% lower than TanStack Table125% higher than Infinite Table
AG Grid4,141 ms4,141 msAG GridString column, descending24% lower than MUI X Data Grid29% lower than TanStack Table70% higher than Infinite Table
TanStack Table5,848 ms5,848 msTanStack TableString column, descending7% higher than MUI X Data Grid41% higher than AG Grid141% higher than Infinite Table
Infinite Table2,431 ms2,431 msInfinite TableString column, descending56% lower than MUI X Data Grid41% lower than AG Grid58% lower than TanStack Table
Number column, ascending
MUI X Data Grid2,596 ms2,596 msMUI X Data GridNumber column, ascending21% lower than AG Grid42% lower than TanStack Table89% higher than Infinite Table
AG Grid3,306 ms3,306 msAG GridNumber column, ascending27% higher than MUI X Data Grid27% lower than TanStack Table141% higher than Infinite Table
TanStack Table4,510 ms4,510 msTanStack TableNumber column, ascending74% higher than MUI X Data Grid36% higher than AG Grid228% higher than Infinite Table
Infinite Table1,374 ms1,374 msInfinite TableNumber column, ascending47% lower than MUI X Data Grid58% lower than AG Grid70% lower than TanStack Table
Number column, descending
MUI X Data Grid3,760 ms3,760 msMUI X Data GridNumber column, descending11% higher than AG Grid17% lower than TanStack Table154% higher than Infinite Table
AG Grid3,379 ms3,379 msAG GridNumber column, descending10% lower than MUI X Data Grid25% lower than TanStack Table128% higher than Infinite Table
TanStack Table4,527 ms4,527 msTanStack TableNumber column, descending20% higher than MUI X Data Grid34% higher than AG Grid206% higher than Infinite Table
Infinite Table1,479 ms1,479 msInfinite TableNumber column, descending61% lower than MUI X Data Grid56% lower than AG Grid67% lower than TanStack Table
View as table
Sorting 1,000,000 rows: click to sorted rows paintedMUI X Data GridAG GridTanStack TableInfinite Table
String column, ascending5,466 ms4,067 ms6,083 ms2,120 ms
String column, descending5,478 ms4,141 ms5,848 ms2,431 ms
Number column, ascending2,596 ms3,306 ms4,510 ms1,374 ms
Number column, descending3,760 ms3,379 ms4,527 ms1,479 ms
Infinite Table is the fastest in all sixteen combinations: 1.6-2.6ร— faster than MUI X Data Grid, 1.7-2.4ร— faster than AG Grid and 2.4-3.3ร— faster than TanStack Table. Almost all of this time is JavaScript on the main thread for every grid, so this is a measure of the sorting pipeline, not of rendering.
The browser ran with the en-US locale, which matters when comparing strings. In that locale, sorting 1M rows by the string column (ascending) took 2120 ms in Infinite Table, 4067 ms in AG Grid, 5466 ms in MUI X Data Grid and 6083 ms in TanStack Table.

Jumping around the dataset#

Ten programmatic jumps to far-away scroll positions, similar to dragging the scrollbar thumb.
Jumping to a far-away scroll position
jump to rows painted, median of 10 jumps ยท lower is better
300k rows
MUI X Data Grid23.6 ms23.6 msMUI X Data Grid300k rows32% lower than AG Grid93% higher than TanStack Table33% lower than Infinite Table
AG Grid34.9 ms34.9 msAG Grid300k rows48% higher than MUI X Data Grid186% higher than TanStack Table1% lower than Infinite Table
TanStack Table12.2 ms12.2 msTanStack Table300k rows48% lower than MUI X Data Grid65% lower than AG Grid65% lower than Infinite Table
Infinite Table35.3 ms35.3 msInfinite Table300k rows50% higher than MUI X Data Grid1% higher than AG Grid189% higher than TanStack Table
1M rows
MUI X Data Grid21.8 ms21.8 msMUI X Data Grid1M rows38% lower than AG Grid74% higher than TanStack Table45% lower than Infinite Table
AG Grid34.9 ms34.9 msAG Grid1M rows60% higher than MUI X Data Grid179% higher than TanStack Table13% lower than Infinite Table
TanStack Table12.5 ms12.5 msTanStack Table1M rows43% lower than MUI X Data Grid64% lower than AG Grid69% lower than Infinite Table
Infinite Table40 ms40 msInfinite Table1M rows83% higher than MUI X Data Grid15% higher than AG Grid220% higher than TanStack Table
View as table
Jumping to a far-away scroll positionMUI X Data GridAG GridTanStack TableInfinite Table
300k rows23.6 ms34.9 ms12.2 ms35.3 ms
1M rows21.8 ms34.9 ms12.5 ms40 ms
TanStack Table is the clear winner here: the rows are on screen 12 ms after the jump. MUI X Data Grid needs 22-24 ms, and AG Grid and Infinite Table about 35 ms - level with each other at 300k rows, while at 1M rows AG Grid is 5 ms faster than Infinite Table.
Then there is a problem you only hit at this scale: 1,000,000 rows ร— 40 px is 40 million pixels, taller than browsers allow an element to be (about 33.5 million px in Chrome on a standard-density display, and half of that on a 2ร— display).
  • Infinite Table and AG Grid map the scrollbar onto the rows - 1.25 content pixels per scrollbar pixel in this run - and the last row is reachable in both.
  • MUI X Data Grid and TanStack Virtual don't: the element is cut at the browser's limit, and the last row you can scroll to is 838,860. The 161,140 rows after it are loaded and sorted, but can't be seen. Their 1M scrolling numbers are for the part that can be reached.

Scrolling smoothness#

A wheel gesture scrolling 24,000 px down at 8,000 px/s, and horizontal sweeps across all columns at 4,000 px/s.
Scrolling horizontally, all four grids hold 60 fps at both sizes, and the slowest frame of a typical run is one refresh interval (17 ms).
Scrolling vertically, MUI X Data Grid, AG Grid and TanStack Table hold 59-60 fps. Infinite Table is at 59 fps with 300k rows and at 55.5 fps with a million, and it has the longest single frame of the four: 50 ms at 300k rows and 117 ms at 1M, where the others stay at 17 ms (AG Grid: 33 ms at 1M). That is one hitch in a three-second gesture, on a machine without a GPU; it is the first thing we will look at.
Infinite Table does get more of its frames fully presented while scrolling vertically - frames that include the grid's latest update, instead of the compositor scrolling what it already had:
Vertical scrolling: frames fully presented
share of frames that include the grid's latest update ยท higher is better
300k rows
MUI X Data Grid50.3%50.3%MUI X Data Grid300k rows2% lower than AG Gridsame as TanStack Table15% lower than Infinite Table
AG Grid51.1%51.1%AG Grid300k rows2% higher than MUI X Data Grid2% higher than TanStack Table14% lower than Infinite Table
TanStack Table50.3%50.3%TanStack Table300k rowssame as MUI X Data Grid2% lower than AG Grid15% lower than Infinite Table
Infinite Table59.1%59.1%Infinite Table300k rows17% higher than MUI X Data Grid16% higher than AG Grid17% higher than TanStack Table
1M rows
MUI X Data Grid50.3%50.3%MUI X Data Grid1M rows1% higher than AG Gridsame as TanStack Table23% lower than Infinite Table
AG Grid49.9%49.9%AG Grid1M rows1% lower than MUI X Data Grid1% lower than TanStack Table23% lower than Infinite Table
TanStack Table50.3%50.3%TanStack Table1M rowssame as MUI X Data Grid1% higher than AG Grid23% lower than Infinite Table
Infinite Table65.2%65.2%Infinite Table1M rows30% higher than MUI X Data Grid31% higher than AG Grid30% higher than TanStack Table
View as table
Vertical scrolling: frames fully presentedMUI X Data GridAG GridTanStack TableInfinite Table
300k rows50.3%51.1%50.3%59.1%
1M rows50.3%49.9%50.3%65.2%
The other difference is in how much work the main thread does to keep up - which matters on slower machines and when your own cell renderers add to it.
Vertical scrolling, 300k rows: main-thread work
total over a 3-second gesture, median of 7 runs ยท lower is better
Scripting
MUI X Data Grid677 ms677 msMUI X Data GridScripting3% higher than AG Grid58% higher than TanStack Table12% lower than Infinite Table
AG Grid660 ms660 msAG GridScripting3% lower than MUI X Data Grid54% higher than TanStack Table14% lower than Infinite Table
TanStack Table428 ms428 msTanStack TableScripting37% lower than MUI X Data Grid35% lower than AG Grid44% lower than Infinite Table
Infinite Table771 ms771 msInfinite TableScripting14% higher than MUI X Data Grid17% higher than AG Grid80% higher than TanStack Table
Layout
MUI X Data Grid72 ms72 msMUI X Data GridLayout35% lower than AG Grid22% lower than TanStack Table18% lower than Infinite Table
AG Grid111 ms111 msAG GridLayout54% higher than MUI X Data Grid21% higher than TanStack Table26% higher than Infinite Table
TanStack Table92.1 ms92.1 msTanStack TableLayout28% higher than MUI X Data Grid17% lower than AG Grid5% higher than Infinite Table
Infinite Table87.8 ms87.8 msInfinite TableLayout22% higher than MUI X Data Grid21% lower than AG Grid5% lower than TanStack Table
Style recalculation
MUI X Data Grid100 ms100 msMUI X Data GridStyle recalculation66% lower than AG Grid102% higher than TanStack Table50% lower than Infinite Table
AG Grid296 ms296 msAG GridStyle recalculation196% higher than MUI X Data Grid498% higher than TanStack Table47% higher than Infinite Table
TanStack Table49.5 ms49.5 msTanStack TableStyle recalculation51% lower than MUI X Data Grid83% lower than AG Grid75% lower than Infinite Table
Infinite Table202 ms202 msInfinite TableStyle recalculation102% higher than MUI X Data Grid32% lower than AG Grid308% higher than TanStack Table
All tasks
MUI X Data Grid1,625 ms1,625 msMUI X Data GridAll tasks0% lower than AG Grid60% higher than TanStack Table7% lower than Infinite Table
AG Grid1,630 ms1,630 msAG GridAll tasks0% higher than MUI X Data Grid61% higher than TanStack Table6% lower than Infinite Table
TanStack Table1,013 ms1,013 msTanStack TableAll tasks38% lower than MUI X Data Grid38% lower than AG Grid42% lower than Infinite Table
Infinite Table1,739 ms1,739 msInfinite TableAll tasks7% higher than MUI X Data Grid7% higher than AG Grid72% higher than TanStack Table
View as table
Vertical scrolling, 300k rows: main-thread workMUI X Data GridAG GridTanStack TableInfinite Table
Scripting677 ms660 ms428 ms771 ms
Layout72 ms111 ms92.1 ms87.8 ms
Style recalculation100 ms296 ms49.5 ms202 ms
All tasks1,625 ms1,630 ms1,013 ms1,739 ms
Scrolling vertically, TanStack Table does about 40% less work than the three full grids. Among those, Infinite Table does the most at 300k rows - 7% more than MUI X Data Grid and AG Grid, because it runs more JavaScript per frame, even though it does less layout and style recalculation than AG Grid. At 1M rows MUI X Data Grid does the least of the three, and Infinite Table 3% less than AG Grid.
Horizontal scrolling, 300k rows: main-thread work
total over 4 sweeps across all columns, median of 7 runs ยท lower is better
Scripting
MUI X Data Grid401 ms401 msMUI X Data GridScripting49% higher than AG Grid127% higher than TanStack Table143% higher than Infinite Table
AG Grid270 ms270 msAG GridScripting33% lower than MUI X Data Grid53% higher than TanStack Table64% higher than Infinite Table
TanStack Table177 ms177 msTanStack TableScripting56% lower than MUI X Data Grid34% lower than AG Grid7% higher than Infinite Table
Infinite Table165 ms165 msInfinite TableScripting59% lower than MUI X Data Grid39% lower than AG Grid7% lower than TanStack Table
Layout
MUI X Data Grid49.1 ms49.1 msMUI X Data GridLayout38% lower than AG Grid10% lower than TanStack Table313% higher than Infinite Table
AG Grid78.9 ms78.9 msAG GridLayout61% higher than MUI X Data Grid45% higher than TanStack Table563% higher than Infinite Table
TanStack Table54.6 ms54.6 msTanStack TableLayout11% higher than MUI X Data Grid31% lower than AG Grid359% higher than Infinite Table
Infinite Table11.9 ms11.9 msInfinite TableLayout76% lower than MUI X Data Grid85% lower than AG Grid78% lower than TanStack Table
Style recalculation
MUI X Data Grid24.5 ms24.5 msMUI X Data GridStyle recalculation48% lower than AG Grid56% higher than TanStack Table54% lower than Infinite Table
AG Grid46.7 ms46.7 msAG GridStyle recalculation91% higher than MUI X Data Grid197% higher than TanStack Table13% lower than Infinite Table
TanStack Table15.7 ms15.7 msTanStack TableStyle recalculation36% lower than MUI X Data Grid66% lower than AG Grid71% lower than Infinite Table
Infinite Table53.4 ms53.4 msInfinite TableStyle recalculation118% higher than MUI X Data Grid14% higher than AG Grid240% higher than TanStack Table
All tasks
MUI X Data Grid749 ms749 msMUI X Data GridAll tasks4% higher than AG Grid39% higher than TanStack Table51% higher than Infinite Table
AG Grid721 ms721 msAG GridAll tasks4% lower than MUI X Data Grid34% higher than TanStack Table46% higher than Infinite Table
TanStack Table538 ms538 msTanStack TableAll tasks28% lower than MUI X Data Grid25% lower than AG Grid9% higher than Infinite Table
Infinite Table495 ms495 msInfinite TableAll tasks34% lower than MUI X Data Grid31% lower than AG Grid8% lower than TanStack Table
View as table
Horizontal scrolling, 300k rows: main-thread workMUI X Data GridAG GridTanStack TableInfinite Table
Scripting401 ms270 ms177 ms165 ms
Layout49.1 ms78.9 ms54.6 ms11.9 ms
Style recalculation24.5 ms46.7 ms15.7 ms53.4 ms
All tasks749 ms721 ms538 ms495 ms
Scrolling horizontally at 300k rows, Infinite Table does the least work: 8% less than TanStack Table and about a third less than AG Grid and MUI X Data Grid, with 4-7ร— less layout than any of them. At 1M rows TanStack Table does the least, 17% less than Infinite Table, which stays ahead of AG Grid (by 14%) and MUI X Data Grid (by 29%).

What you see during a very fast scroll#

This is where the grids make genuinely different design choices.
When you flick the wheel faster than a grid can re-render, it has to show something for the rows it hasn't caught up with. There are two reasonable options:
  • Show empty space until the right content is rendered. Nothing on screen is ever wrong, but you can see gaps at the edge of the viewport.
  • Move the existing rows into place immediately and update their content on the next render. The viewport is always full, but for a frame or two some rows can show the content they held before.
AG Grid and TanStack Table take the first approach. Infinite Table takes the second for vertical scrolling: it repositions rows as soon as the scroll happens, without waiting for React to commit the new content, so that strong inertial scrolling never reveals blank areas. For horizontal scrolling it waits for the commit.
The benchmark measures both effects during a 20,000 px/s flick. At 1M rows, 53% of the frames captured from AG Grid and 10% of those from TanStack Table had blank rows, and none of those from Infinite Table or MUI X Data Grid. In Infinite Table, most of the points sampled in its rows during the flick showed a row's earlier content; MUI X Data Grid kept up with neither blank nor stale rows. All of those numbers are in the full report.

What we take away from this#

Where Infinite Table came out ahead of all three:
  • Sorting: 1.6-2.6ร— faster than MUI X Data Grid, 1.7-2.4ร— faster than AG Grid, 2.4-3.3ร— faster than TanStack Table (strings sorted in an en-US browser)
  • Memory: 32-40% less than MUI X Data Grid, 46-57% less than AG Grid, 58-68% less than TanStack Table
  • Mounting a million rows: 14-30% sooner
  • Main-thread work while scrolling horizontally at 300k rows
  • More frames fully presented while scrolling vertically
  • Reaching the last of a million rows, which only AG Grid also does
Where MUI X Data Grid came out ahead of Infinite Table:
  • Jumping to a far-away row: 22-24 ms against 35-40 ms
  • Vertical scrolling: 7-19% less main-thread work, a steady 60 fps, and no long frame
  • A very fast scroll with neither blank nor stale rows
Where AG Grid came out ahead of Infinite Table:
  • Vertical scrolling: 59-60 fps and a shorter longest frame
  • A faster median jump at 1M rows (by 5 ms)
  • 6% less main-thread work scrolling vertically at 300k rows
Where TanStack Table (with TanStack Virtual) came out ahead of Infinite Table:
  • Jumping to a far-away row: 12 ms against 35-40 ms
  • Vertical scrolling: about 45% less main-thread work, and a steady 60 fps
  • Mounting 300k rows 6% faster, and less work scrolling horizontally at 1M rows
It does that with a page that is not a full grid.
For us, there are two clear to-dos, both in vertical scrolling: the long frame, and the JavaScript we run per scrolled frame. We're glad to see where we're ahead, and just as glad to have a precise, repeatable measure of where we're not.

Limitations#

A benchmark is a model of reality, not reality. Please keep these in mind:
  • One machine, one browser. A shared cloud machine with 4 cores and no GPU. A laptop will be faster in absolute terms, and its frame pacing will be different. Compare the ratios between the grids, not the absolute numbers.
  • Headless Chrome. These numbers come from Chrome's new headless mode, which runs the same rendering code as headed Chrome. Frame-pacing numbers can still differ in a headed browser with a real display.
  • Defaults only. Every grid has options that could change these results. We deliberately didn't tune any of them, because tuning one grid well and another badly is the easiest way to make a benchmark unfair.
  • Not four of the same thing. The TanStack Table page is an example with our markup, not a full grid; a different way of rendering TanStack Table would give different numbers. MUI X Data Grid is on one page that holds every row, which is not its default.
  • One dataset shape. 20 columns of plain text and numbers, no custom cell renderers, no grouping, no filtering, no live updates. Real apps do more.
  • One browser locale. The string sorting result is for an en-US browser.
  • We have an interest. We make Infinite Table. We've tried to design the harness so that it measures every grid the same way, and to report every metric - including the ones where another grid is ahead - but you shouldn't have to trust us. That's what the next section is for.

Run it yourself#

The benchmark has its own repository: infinite-table/datagrid-performance-comparison. All four grids are installed from npm, so there is nothing to build but the benchmark pages. Its README also has the results for the latest versions: the benchmark runs again on GitHub Actions whenever a new version of Infinite Table or one of the grids it is compared with is published. You need git and Node.js 20 or newer (we used 22); it runs on macOS and Linux. Playwright downloads its own Chromium, so it doesn't matter which browser you have installed.

1. Get the code and install#

git clone https://github.com/infinite-table/datagrid-performance-comparison.git
cd datagrid-performance-comparison
npm install
npx playwright install chromium

2. Run it#

# production build, then every scenario at 300k and 1M rows
BENCH_HEADLESS=1 npm run benchmark
A full run takes about an hour: 9 scenarios ร— 2 sizes ร— 4 grids ร— 8 runs. Without BENCH_HEADLESS=1 it opens real browser windows - closer to what users see, but you have to leave the machine alone while they come and go.
For numbers you can trust, close other apps, keep a laptop plugged in, and don't use the machine during the run.
To try it first, this takes about a minute:
npm run benchmark:build
# mount and string sorting, 300k rows only, 3 runs per grid
BENCH_HEADLESS=1 BENCH_SIZES=300k BENCH_SCENARIOS=mount,sort-string BENCH_RUNS=3 npm run benchmark:run

3. Read the results#

Every run writes two files to benchmark-results/<run id>/:
  • report.md - one table per scenario, with the median and standard deviation for each grid and, for Infinite Table against each of the others, the ratio, its 95% confidence interval, and which grid is better when the difference is significant
  • results.json - every raw sample of every run
The most recent report is also copied to benchmark-results/latest.md.

Compare other versions#

The versions that are measured are the ones installed in node_modules, and the report records them. To compare other releases of any of the grids, install them and run the benchmark again:
npm install @infinite-table/infinite-react@<version>
npm install @mui/x-data-grid@<version>
npm install ag-grid-community@<version> ag-grid-react@<version>
npm install @tanstack/react-table@<version> @tanstack/react-virtual@<version>

BENCH_HEADLESS=1 npm run benchmark
Infinite Table reaches the last of a million rows starting with version 10.1.4. To measure only some of the grids, name them: BENCH_GRIDS=mui,infinite.

Options#

VariableDefault
BENCH_SIZES300k,1mdataset sizes to run
BENCH_SCENARIOSallmount, sort-string, sort-number, scroll-jump, scroll-vertical, scroll-horizontal, correctness-vertical, correctness-horizontal, visual-blank-vertical
BENCH_GRIDSallmui, ag-grid, tanstack, infinite - at least two
BENCH_RUNS7measured runs per grid per scenario
BENCH_HEADLESSoff1 runs without opening browser windows
BENCH_CPU_THROTTLE14 slows the CPU 4ร—, closer to a mid-range laptop
The README documents the rest, along with the methodology of every scenario.

See the grids yourself#

The pages the benchmark measures are ordinary pages you can open and scroll yourself. After a build:
npm run benchmark:serve
Then open localhost:3000 for the list of pages: /benchmark/infinite-1m, /benchmark/mui-1m, /benchmark/ag-grid-1m and /benchmark/tanstack-1m (or -300k). If port 3000 is taken, pick another one with npm run benchmark:serve -- -p 4000.

The full report#

The report and raw data behind this post:
Both are also in the benchmark repository, under results/blog-post-2026-11-12.

Help us make it fairer#

If you find a flaw in the methodology, a setting that unfairly favors one grid, or a scenario that's missing, please open an issue or send a pull request. That goes double for the MUI X, AG Grid and TanStack teams: if there's a configuration you'd recommend for a fair comparison - or a better way to render TanStack Table - we'll gladly add it, re-run the numbers, and update this post. You can also reach us at admin@infinite-table.com.