Tables Bakeoff

A 2026 bake-off of JavaScript table and data-grid libraries. Every library renders the same four datasets — up to 500,000 rows of real property-sale data — through the same harness, with the same theme tokens and the same shared cell formatter. So what you are comparing is the library, not someone's styling effort. The source of every render function is on its own page.

A sibling of line-charts, built the same way. Numbers below are measured, not quoted: bundle sizes and line counts are read from bundles.json, which this build writes.

The seven contenders

Plain <table>

No library. Hand-built DOM, sticky header, ~20 lines. The control.

no dependency · n/a

Observable Inputs

One function call. Strong defaults, built for data exploration.

v0.12.0 · ISC

Tabulator

Vanilla, batteries-included: grouping, tree data, editing, export.

v6.5.2 · MIT

AG Grid Community

The enterprise default. Vanilla core, row + column virtualization.

v36.1.0 · MIT (Community tier)

TanStack Table

Headless. It computes rows and sorting; you write every element.

v9.2.4 · MIT · React

Glide Data Grid

Canvas-rendered, React-only. Spreadsheet feel at any row count.

v6.0.3 · MIT · React

Perspective

WASM + Arrow columnar engine in a worker, with a grid attached.

v5.3.1 · Apache-2.0

What was measured

Facts only. Every column here is something this project actually measured — no ratings, no stars, no scores out of five. The judgement calls (default look, API ergonomics, documentation quality) are deliberately not on this page; see the gap note below.

Library Bundle
(gzip, own code)
WASM Lines
written
Render
50,000
Render
500,000
Scroll FPS
at 500,000
Survived
500,000?
Licence
Plain <table> 2,625 ms never finishedcapped at 100,000 → 6.9 s n/a15 fps at 50,000 No n/a
Observable Inputs 5.8 msa 6.7 msa 60 fps Yesa ISC
Tabulator 68 msb 280 msb 60 fps Yes MIT
AG Grid Community 35 ms 248 ms 60 fps Yes MIT (Community)
TanStack Table 20.0 ms 141 ms 59 fps Yes MIT
Glide Data Grid 17.4 ms 24.6 ms 60 fps Yesc MIT
Perspective 274 ms 2,100–2,700 msd 60 fps Yes Apache-2.0
The verdict is not written yet. The subjective axes — how good the default output looks, how the API feels to use, how good the documentation is, accessibility in practice — are the whole reason to run a bake-off, and they are not on this page because nobody has written them yet. They belong in EVALUATION.md, which does not exist at the time of writing. A measured table with an honest gap beats a confident table of invented ratings, so the gap is left visible. Until then: read the notes on each demo page, which record what actually happened while building it.

Quick picks

What the measurements showed

The four datasets

Real HM Land Registry Price Paid data, converted to Parquet and served as static files. Every library gets all four, from the same loader, as the same array of row objects.

Method

Same four datasets, same theme tokens, same shared cell formatter, same mounting harness for all seven. Each demo supplies four render functions and nothing else; the harness owns the page, the card layout, the clock and the FPS run, and empties the host between renders. The render clock awaits async work and forces layout before stopping, so a library that returns early cannot report a scheduling call as a render. The FPS run scrolls the element that actually moves — each demo marks its own internal viewport — because measuring a container that never scrolls returns a flat 60 fps for nothing.

The one exception: Perspective's cells do not pass through the shared formatter, because it exposes no per-cell hook — only Intl option bags per column. That is a finding about the library rather than a flaw in the method, but it means its columns are the one place on this site where a visible difference is not attributable to the library's rendering alone.

Considered, not included

Experiments

Exploratory spikes that are deliberately not part of the seven-way comparison above — different questions, not an eighth library card.

Summary + filter table

Per-column summary stats (mini histogram, min/max/mean) above the table, plus in-table filtering, trialed on TanStack.

DuckDB query demo

Run arbitrary SQL against the full 500,000-row dataset with DuckDB-WASM, rendered into TanStack Table.

Contains HM Land Registry data © Crown copyright and database right 2026. This data is licensed under the Open Government Licence v3.0.

Add a library: copy demos/baseline.html and src/demos/baseline/main.js, export meta and four render functions, add the key to DEMOS in vite.config.js, and add a card and a row here. Bundle size and line count then appear by themselves.