Compared to Other Grids
This page compares @toolbox-web/grid against three widely-used JavaScript data grids: AG Grid, Tabulator, and SlickGrid. The goal is an honest side-by-side on features, bundle size, and — below — measured performance in your own browser.
Why these three?
Section titled “Why these three?”They bracket the market. AG Grid sets the upper bound on feature breadth and bundle size, Tabulator is the mainstream MIT option, and SlickGrid (the maintained 6pac fork) the minimalist, virtualization-first one. Framework-locked grids — MUI DataGrid, PrimeNG Table, Vuetify DataTable — are out of scope: @toolbox-web/grid is framework-agnostic, and comparing it only against those would skew the picture.
Sources and disclaimer
Feature availability comes from each vendor’s public documentation — AG Grid, Tabulator, and SlickGrid (6pac fork v2.x). Handsontable and TreeGrid are not in the feature table; they appear only in the pricing table and the alternatives named further down. All figures last verified 9 September 2026.
AG Grid is a trademark of AG Grid Ltd; Handsontable is a trademark of Handsoncode; TreeGrid and FastGrid are products of Coqsoft. This is an independent comparison, not affiliated with, endorsed by, or sponsored by any listed project. Verify current features and pricing on each project’s own site.
Quick Summary
Section titled “Quick Summary”| Aspect | @toolbox-web/grid | AG Grid | Tabulator | SlickGrid |
|---|---|---|---|---|
| License | MIT | MIT (Community) / Commercial (Enterprise) | MIT | MIT (6pac fork) |
| Cost | Free | Free / $999 per developer (Enterprise) | Free | Free |
| Bundle Size (gzipped) | ~47 KB | ~200 KB+ (Community) · ~300 KB+ (Enterprise) | ~90 KB | ~30 KB + jQuery |
| Dependencies | Zero | Zero | Zero | jQuery |
| Framework Support | All (Web Component) | All (wrappers) | All (wrappers) | Vanilla / jQuery |
| TypeScript | ✅ Bundled | ✅ Bundled | ✅ Bundled | Community types |
Feature Comparison
Section titled “Feature Comparison”Legend: ✅ first-party support (built-in or official plugin/module) · ⚙️ requires third-party extension or manual wiring · ❌ not available · For AG Grid, values are shown as Community / Enterprise when they differ.
| Feature | @toolbox-web/grid | AG Grid (CE / Ent) | Tabulator | SlickGrid |
|---|---|---|---|---|
| Row Virtualization | ✅ | ✅ | ✅ | ✅ |
| Column Virtualization | ✅ | ✅ | ✅ | ✅ |
| Sorting (Single & Multi) | ✅ | ✅ | ✅ | ✅ |
| Column Resizing | ✅ | ✅ | ✅ | ✅ |
| Column Reordering | ✅ | ✅ | ✅ | ✅ |
| Column Pinning/Freezing | ✅ | ✅ | ✅ | ✅ |
| Row Pinning | ✅ | ✅ | ✅ | ✅ |
| Cell Selection | ✅ | ✅ | ✅ | ✅ |
| Row Selection | ✅ | ✅ | ✅ | ✅ |
| Checkbox Selection | ✅ | ✅ | ✅ | ✅ |
| Custom Cell Renderers | ✅ | ✅ | ✅ | ✅ |
| Cell Editing | ✅ | ✅ | ✅ | ✅ |
| Custom Editors | ✅ | ✅ | ✅ | ✅ |
| Keyboard Navigation | ✅ | ✅ | ✅ | ✅ |
| Themes/Styling | ✅ (6 built-in) | ✅ | ✅ (5 built-in) | Minimal |
| CSV Export | ✅ | ✅ | ✅ | ✅ |
| Filtering | ✅ | ✅ | ✅ | ✅ |
| Set / Multi-value Filters | ✅ | ❌ / ✅ Paid | ✅ | ⚙️ |
| Pagination | ⚙️ (virtualized by default — no pager UI) | ✅ | ✅ | ⚙️ |
| Column Visibility | ✅ | ✅ | ✅ | ✅ |
| Column & Filter Tool Panels | ✅ | ❌ / ✅ Paid | ⚙️ | ⚙️ |
| Row Animations | ✅ | ✅ | Limited | ❌ |
| Range Selection | ✅ | ❌ / ✅ Paid | ✅ | ⚙️ |
| Clipboard (Copy/Paste) | ✅ | ❌ / ✅ Paid | ✅ | ⚙️ |
| Excel Export | ✅ | ❌ / ✅ Paid | ✅ (basic) | ⚙️ |
| Row Grouping | ✅ | ❌ / ✅ Paid | ✅ | ✅ |
| Aggregation/Footers | ✅ | ❌ / ✅ Paid | ✅ | ✅ |
| Pivot Table | ✅ | ❌ / ✅ Paid | ❌ | ❌ |
| Tree Data | ✅ | ❌ / ✅ Paid | ✅ | ✅ |
| Master-Detail | ✅ | ❌ / ✅ Paid | ✅ | ⚙️ |
| Server-Side Row Model | ✅ | ❌ / ✅ Paid | ✅ (progressive) | Manual |
| Infinite Scrolling | ✅ | ✅ | ✅ | Manual |
| Context Menu | ✅ | ❌ / ✅ Paid | ⚙️ | ⚙️ |
| Column Groups (spanning headers) | ✅ | ✅ | ✅ | ✅ |
| Cell Spanning (row/col span in the body) | ❌ | ✅ | ❌ | ⚙️ (getItemMetadata) |
| Row Drag & Drop | ✅ | ✅ | ✅ | ⚙️ |
| ✅ | ❌ / ✅ Paid | ✅ | ❌ | |
| Undo/Redo | ✅ | ✅ | ✅ (history) | ❌ |
| Responsive/Mobile Layout | ✅ (card layout) | Manual | ✅ (collapse) | Manual |
| Framework Agnostic | ✅ Native Web Component | Wrapper-based | Wrapper-based | jQuery-based |
| Tree-Shakeable Plugins | ✅ | Partial | Partial (modules) | ❌ |
| Styled Excel Export (fonts, fills, number formats, merges) | ✅ | ❌ / ✅ Paid | ❌ | ❌ |
| Integrated Charts | ⚙️ (bring your own chart library) | ❌ / ✅ Paid | ❌ | ❌ |
| Built-in Sparklines | ⚙️ (recipe) | ❌ / ✅ Paid | ❌ | ❌ |
| End-user Cell Formulas | ⚙️ (recipe) | ❌ / ✅ Paid | ❌ | ❌ |
| Dedicated Support with SLAs | ❌ | ❌ / ✅ Paid | ❌ | ❌ |
Performance
Section titled “Performance”Pick a competitor from the dropdown, then click Run Comparison. It iterates through increasing data sizes (5K → 1M rows), running Toolbox Grid and the chosen competitor sequentially in your browser with equivalent data and configuration. Results are cached per competitor for the lifetime of the page, so you can switch back without re-running.
Show Grid Configurations
Toolbox Grid
// Configuration displayed hereCompetitor
// Configuration displayed hereGrid will render here during benchmarks (one at a time)
Pick a competitor and click Run Comparison to benchmark both grids at increasing data sizes.
Scale points: 5K → 100K → 500K → 1M rows × 10 columns. Macro ops: cold mount to painted viewport, full-dataset sort and filter, and replacement to painted viewport — these drive the headline speedup. Round-trip sums the macro times. Micro ops (within-frame interactions): single-row update, column resize, scroll-to-end, destroy — reported separately because most ties cluster at the ~16 ms frame floor on every virtualized grid. Viewport overhead: DOM node count after first paint. Every sample is accepted only after an untimed outcome validation; each timed metric runs 5× per scale point (trimmed mean).
How this benchmark works
- The selected competitor and its CSS theme are loaded once from a CDN. The exact version is shown; SlickGrid is pinned to its last global-script release.
- Cold mount starts from an empty host for both grids and is measured through the browser’s next paint opportunity.
- Each timed metric runs five times and reports a trimmed mean. Sort inputs use identical seeded permutations.
- Execution order alternates at each scale point to reduce systematic warmup, thermal, and garbage-collection bias. Cooldowns reduce immediate contention but do not guarantee garbage collection.
- Every sample has an untimed semantic postcondition. The run aborts instead of publishing a number when mounting, sorting, filtering, replacement, update, resize, navigation, or teardown does not produce the required outcome.
- Results build up progressively, showing the scaling curve. Switching the dropdown shows cached results instantly; you only re-run when you click the button again.
Expand Show Grid Configurations in the benchmark to inspect the settings. Each competitor has its own adapter under apps/docs/src/components/demos/competitors/; adapters use the closest public API for the same user-visible outcome. Browser results remain indicative rather than controlled laboratory measurements.
Portable benchmark contract
Section titled “Portable benchmark contract”The headline compares only operations with a meaningful public-API equivalent across all included grids:
| Operation | Timed interval | Required postcondition |
|---|---|---|
| Cold mount | Empty host to painted viewport | Full source count is modeled and at least one row is painted |
| Sort | Request descending numeric sort to painted viewport | First and last model rows have the expected IDs |
| Filter | Request id > rowCount / 2 to painted viewport | Active count is exactly half and surviving IDs satisfy the predicate |
| Replace | Assign marked replacement data to painted viewport | Full count is modeled and the marker is visible |
| Update | Keyed row patch to painted viewport | The addressed model row contains the new value |
| Resize | Set one column width to painted viewport | The public column state reports the requested width |
| Navigate | Request the final row to painted viewport | The scrolling viewport reaches its end |
| Destroy | Public teardown plus host removal to painted viewport | The benchmark host is disconnected |
DOM-node count is a separate viewport-overhead statistic, not a memory measurement. Frame-bound update, resize, navigation, and destroy timings are reported separately from the macro headline. Grouping, pivoting, editing, tree data, export, and multi-column sorting are excluded — they are not universal enough across the four grids to score fairly, and showing an unsupported capability as a zero-time win would be dishonest.
Only free grids are in the chart. AG Grid Enterprise, Handsontable, and TreeGrid all need a licence key, and re-hosting a build so your browser could run it would break the terms of the products we’d be comparing against. AG Grid Community is included, and Enterprise is modules layered on the same rendering core.
SlickGrid is pinned to v2.4.x (the last release with the global Slick namespace) and pulls jQuery from CDN. Column reorder and drag are disabled to keep the dependency surface minimal.
Framework freedom
Section titled “Framework freedom”@toolbox-web/grid is a native Web Component, not a wrapper around a framework-specific core. The same element and the same API work in vanilla JS/TypeScript, React, Angular, Vue, and Svelte. The optional adapters exist so you can write renderers and editors in your framework’s own idiom — JSX, Angular templates, Vue slots — not to make the grid work at all.
Coming from a commercial grid
Section titled “Coming from a commercial grid”Most per-developer grid licences are justified by the same handful of features: row grouping with aggregation, pivot, tree data, master-detail, a server-side row model, range selection and clipboard, context menus, tool panels, styled Excel export. Those are the rows marked ❌ / ✅ Paid in the feature table above, and all of them are MIT here. No licence key, no seat count. Two rows run the other way — cell spanning and an SLA — and both are covered under When not to choose Toolbox Grid.
Handsontable and TreeGrid split things differently. Both are spreadsheet products first, so a row-for-row comparison doesn’t really hold up — check Handsontable’s feature list and TreeGrid’s yourself rather than trusting our summary of them. Short version: if your users type formulas, you want one of those, not this.
What the licences cost
Section titled “What the licences cost”| Product | Licence | Published list price | Source |
|---|---|---|---|
| Toolbox Grid | MIT | $0 — no tiers, no seats, no licence key | LICENSE |
| AG Grid Community | MIT | $0 | ag-grid.com/license-pricing |
| AG Grid Enterprise | Commercial | $999 per developer (perpetual, incl. 1 year of updates & support) | ag-grid.com/license-pricing |
| AG Grid + AG Charts Enterprise Bundle | Commercial | $1,498 per developer | ag-grid.com/license-pricing |
| Handsontable | Commercial | from $999 per developer / year | handsontable.com/pricing |
| TreeGrid Standard | Commercial (perpetual) | from $1,300 for the first developer, plus per-developer additions and 30 %/year maintenance | treegrid.com/Prices |
Prices verified 9 September 2026. List prices in USD, excluding tax, volume discounts, OEM/SaaS redistribution terms, and negotiated enterprise agreements. Confirm on the vendor’s own pricing page before budgeting.
The recurring cost usually matters more than the first invoice. Ten developers on an Enterprise tier is a five-figure yearly line item that grows with the team, plus a licence key to renew and keep out of your public bundle.
When not to choose Toolbox Grid
Section titled “When not to choose Toolbox Grid”Here’s what we don’t do. Where there’s a documented way to get there yourself, it’s linked. Where there isn’t, there’s the name of something that does it properly.
Most of these come down to the same trade-off. The grid has a hard bundle budget and no opinion about which libraries you use. A renderer returns a DOM node and a valueAccessor returns a value, so the charting library, the formula engine and the PDF writer are your call rather than ours. You get to pick, but you also have to wire it up. Plenty of teams would rather it came in the box.
Spreadsheet workloads where users write the formulas
Section titled “Spreadsheet workloads where users write the formulas”There’s no formula engine and we’re not adding one — HyperFormula on its own is roughly four times the size of the whole grid. valueAccessor covers developer-defined computed columns, and those sort, filter, aggregate and export like any other column. The spreadsheet formulas recipe goes further and wires HyperFormula in behind it, including letting users type =SUM(...) into an editable column. It works; it’s just not something we ship and support. If the product you’re building is a spreadsheet, buy a spreadsheet: Handsontable or FastSheet.
Charts and Gantt
Section titled “Charts and Gantt”We don’t ship a chart library, and we won’t. AG Grid bundles AG Charts because they own both; we’d be picking a winner on your behalf and paying for it in the bundle.
The chart-from-grid workflow itself is buildable on public API. selectionPlugin.getSelection().ranges gives you the selected CellRange[], contextMenu.items takes a function so you can add a “Chart selection” entry, and shell.registerToolPanel({ render }) hosts the chart — render returns an optional cleanup function, so Highcharts, ECharts or Chart.js can be destroyed properly when the panel closes. selection-change and cell-commit keep it in sync. For charts inside cells, the sparklines recipe covers inline SVG and uPlot.
What you’re doing yourself is the wiring, the chart-type picker, and the settings UI. There’s no recipe for the full workflow yet. Gantt is a flat no — TreeGrid GanttChart does scheduling, and AG Grid Enterprise Bundle has AG Charts built in if you’d rather it came assembled.
Cell spanning
Section titled “Cell spanning”Not supported. Columns can be grouped under a spanning header, and those spans carry through to the Excel export as merged cells, but a body cell can’t span rows or columns. Row spanning in particular fights the recycled-row virtualization the numbers above depend on, so it’s unlikely to change. AG Grid has it in Community, Handsontable has mergeCells, and SlickGrid does it through getItemMetadata.
Full control over the markup
Section titled “Full control over the markup”The grid renders. It owns the viewport, the row recycler, the row and cell elements and their ARIA roles. You control what goes inside them through renderer, editor, slots and CSS custom properties, but not the elements themselves. If you need to own every node — a non-tabular layout, server-rendered rows, a design system that has to emit its own markup — use something headless. TanStack Table is the obvious one.
Pagination-first UX
Section titled “Pagination-first UX”No pager component, no page-size dropdown, no paginationPageSize. The grid virtualizes, so showing everything is the normal case even at a million rows. ServerSidePlugin fetches in blocks and you can drive it from a pager you build, but you’re building and styling that yourself. If page numbers are a requirement, AG Grid and Tabulator ship one.
A jQuery-native stack
Section titled “A jQuery-native stack”If the app already runs on jQuery and you want the smallest possible footprint, SlickGrid (6pac fork) is a good answer. A web component works there too, but you’d be adding a second paradigm instead of removing one.
Support you can escalate
Section titled “Support you can escalate”No support contract, no SLA, no response-time guarantee. Issues and discussions on GitHub, answered when maintainers get to them. For some organisations that’s a procurement blocker regardless of the feature list. AG Grid Enterprise, Handsontable and TreeGrid all sell support.
Migrating to Toolbox Grid
Section titled “Migrating to Toolbox Grid”Most concepts in the other grids map cleanly to Toolbox Grid. Pick the section for your current grid below.
From AG Grid
Section titled “From AG Grid”| AG Grid | Toolbox Grid |
|---|---|
columnDefs | columns or gridConfig.columns |
rowData | rows |
defaultColDef | Per-type defaults via typeDefaults (keyed by column.type) |
getRowId | getRowId: (row) => string (same shape) |
onCellValueChanged | cell-commit event |
onSelectionChanged | selection-change event |
serverSideRowModel | ServerSidePlugin |
rowGrouping | GroupingRowsPlugin |
masterDetail | MasterDetailPlugin |
Side-by-side AG Grid → Toolbox Grid for the same minimal setup: data, columns, and row selection.
<!-- AG Grid (vanilla) --><div id="myGrid" class="ag-theme-quartz" style="height: 400px;"></div>
<script type="module"> import { createGrid } from 'ag-grid-community';
createGrid(document.querySelector('#myGrid'), { rowData: rows, columnDefs: columns, rowSelection: 'multiple', });</script><!-- Toolbox Grid (vanilla) --><tbw-grid style="height: 400px;"></tbw-grid>
<script type="module"> import '@toolbox-web/grid'; import '@toolbox-web/grid/features/selection'; import { queryGrid } from '@toolbox-web/grid';
const grid = queryGrid('tbw-grid'); grid.gridConfig = { columns, features: { selection: 'row' }, }; grid.rows = rows;</script>// AG Gridimport { AgGridReact } from 'ag-grid-react';
<AgGridReact rowData={rows} columnDefs={columns} rowSelection="multiple"/>// Toolbox Gridimport '@toolbox-web/grid-react/features/selection';import { DataGrid } from '@toolbox-web/grid-react';
<DataGrid rows={rows} columns={columns} selection="row"/><!-- AG Grid --><script setup>import { AgGridVue } from 'ag-grid-vue3';</script>
<template> <AgGridVue :rowData="rows" :columnDefs="columns" rowSelection="multiple" /></template><!-- Toolbox Grid --><script setup>import '@toolbox-web/grid-vue/features/selection';import { TbwGrid } from '@toolbox-web/grid-vue';</script>
<template> <TbwGrid :rows="rows" :columns="columns" selection="row" /></template>// AG Gridimport { Component } from '@angular/core';import { AgGridAngular } from 'ag-grid-angular';
@Component({ imports: [AgGridAngular], template: ` <ag-grid-angular [rowData]="rows" [columnDefs]="columns" rowSelection="multiple"> </ag-grid-angular> `,})export class MyGridComponent {}// Toolbox Gridimport { Component } from '@angular/core';import { Grid } from '@toolbox-web/grid-angular';import { GridSelectionDirective } from '@toolbox-web/grid-angular/features/selection';
@Component({ imports: [Grid, GridSelectionDirective], template: ` <tbw-grid [rows]="rows" [columns]="columns" [selection]="'row'"> </tbw-grid> `,})export class MyGridComponent {}From Tabulator
Section titled “From Tabulator”| Tabulator | Toolbox Grid |
|---|---|
new Tabulator(el, options) | <tbw-grid> element + grid.gridConfig = { ... } |
columns: [{ field, title, ... }] | columns: [{ field, header, ... }] (rename title → header) |
data option / setData(rows) | grid.rows = rows |
index: 'id' | getRowId: (row) => String(row.id) |
layout: 'fitColumns' / 'fitDataFill' | fitMode: 'stretch' / 'fixed' |
setSort([{ column, dir }]) | grid.sort(field, 'asc' | 'desc') |
setFilter(field, type, value) | FilteringPlugin + filterPlugin.setFilterModel([...]) |
cellEdited callback | cell-commit event |
rowSelected callback | selection-change event |
dataTree: true | TreePlugin |
pagination: 'local' | No pagination plugin — the grid virtualizes all rows instead |
<!-- Tabulator --><div id="myGrid" style="height: 400px;"></div>
<script type="module"> import { Tabulator } from 'tabulator-tables';
new Tabulator('#myGrid', { data: rows, columns: [ { title: 'Name', field: 'name', headerSort: true }, { title: 'Email', field: 'email' }, ], selectableRows: true, });</script><!-- Toolbox Grid --><tbw-grid style="height: 400px;"></tbw-grid>
<script type="module"> import '@toolbox-web/grid'; import '@toolbox-web/grid/features/selection'; import { queryGrid } from '@toolbox-web/grid';
const grid = queryGrid('tbw-grid'); grid.gridConfig = { columns: [ { field: 'name', header: 'Name', sortable: true }, { field: 'email', header: 'Email' }, ], features: { selection: 'row' }, }; grid.rows = rows;</script>From SlickGrid
Section titled “From SlickGrid”SlickGrid’s API is lower-level than the others — most behavior is composed from Slick.Grid + Slick.Data.DataView + plugin instances. Toolbox Grid bundles the equivalents into config and built-in plugins.
| SlickGrid | Toolbox Grid |
|---|---|
new Slick.Grid(el, dataView, columns, options) | <tbw-grid> + grid.gridConfig = { columns, ... } |
Slick.Data.DataView | Built into the grid; pass plain arrays to grid.rows |
columns: [{ id, name, field }] | columns: [{ field, header }] (drop separate id; field is the key) |
dataView.setItems(rows, 'id') | grid.rows = rows + getRowId: (row) => String(row.id) |
dataView.sort(comparer, asc) | grid.sort(field, 'asc' | 'desc') |
dataView.setFilter(fn) | FilteringPlugin (declarative filter model) |
Slick.Plugins.RowSelectionModel | features: { selection: 'row' } |
Slick.CheckboxSelectColumn | features: { selection: 'checkbox' } |
Slick.CellRangeSelector + CellSelectionModel | RangeSelectionPlugin |
onCellChange | cell-commit event |
onSelectedRowsChanged | selection-change event |
| Manual jQuery resize handler | Built-in column resize feature |
<!-- SlickGrid --><div id="myGrid" style="height: 400px;"></div>
<script type="module"> // SlickGrid 6pac fork v2.4.x — global Slick namespace const dataView = new Slick.Data.DataView(); dataView.setItems(rows, 'id');
const grid = new Slick.Grid('#myGrid', dataView, [ { id: 'name', name: 'Name', field: 'name', sortable: true }, { id: 'email', name: 'Email', field: 'email' }, ], { enableCellNavigation: true });
grid.setSelectionModel(new Slick.RowSelectionModel());</script><!-- Toolbox Grid --><tbw-grid style="height: 400px;"></tbw-grid>
<script type="module"> import '@toolbox-web/grid'; import '@toolbox-web/grid/features/selection'; import { queryGrid } from '@toolbox-web/grid';
const grid = queryGrid('tbw-grid'); grid.gridConfig = { columns: [ { field: 'name', header: 'Name', sortable: true }, { field: 'email', header: 'Email' }, ], getRowId: (row) => String(row.id), features: { selection: 'row' }, }; grid.rows = rows;</script>See Also
Section titled “See Also”- Getting Started — Install and set up your first grid in minutes
- Demos — Full-featured employee management demo
- Plugins Overview — Complete plugin catalog with feature comparison
- Support & Stability — Supported framework versions, browser baseline, deprecation window, and release cadence