# Compared to Other Grids

> How @toolbox-web/grid stacks up against AG Grid, Tabulator, and SlickGrid — features, bundle size, and a live performance benchmark you can run in your browser.

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.

## 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

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

#### TypeScript

```html
<!-- 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>
```

```html
<!-- 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>
```

#### React

```tsx
// AG Grid
import { AgGridReact } from 'ag-grid-react';

<AgGridReact
  rowData={rows}
  columnDefs={columns}
  rowSelection="multiple"
/>
```

```tsx
// Toolbox Grid
import '@toolbox-web/grid-react/features/selection';
import { DataGrid } from '@toolbox-web/grid-react';

<DataGrid
  rows={rows}
  columns={columns}
  selection="row"
/>
```

#### Vue

```html
<!-- AG Grid -->
<script setup>
import { AgGridVue } from 'ag-grid-vue3';
</script>

<template>
  <AgGridVue
    :rowData="rows"
    :columnDefs="columns"
    rowSelection="multiple"
  />
</template>
```

```html
<!-- 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>
```

#### Angular

```typescript
// AG Grid
import { 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 {}
```

```typescript
// Toolbox Grid
import { 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

| 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: 'auto'` / `'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` | `TreeDataPlugin` |
| `pagination: 'local'` | `PaginationPlugin` |

```html
<!-- 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>
```

```html
<!-- 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

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 |

```html
<!-- 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>
```

```html
<!-- 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>
```

### From TanStack Table

TanStack Table is a headless utility — you bring your own markup, virtualization, and DOM. Toolbox Grid is a rendering web component, so most of TanStack's row-model plumbing (`getSortedRowModel`, `getFilteredRowModel`, `useVirtualizer`) becomes built-in behavior or a one-line feature import.

| TanStack Table | Toolbox Grid |
|----------------|--------------|
| `useReactTable(options)` | Not needed — render `<DataGrid>` directly |
| `columnHelper.accessor('field', …)` | Column config `{ field }` (or `valueAccessor` for computed values) |
| `columnHelper.display({ cell })` | Column with a `renderer` |
| `cell: (info) => …` / `flexRender` | `renderer: (ctx) => …` (returns JSX in React) |
| `getSortedRowModel()` | `sortable: true` (built-in) |
| `getFilteredRowModel()` | `features: { filtering: true }` (`FilteringPlugin`) |
| `getGroupedRowModel()` | `features: { groupingRows: { groupOn } }` |
| `getExpandedRowModel()` | `features: { tree: { childrenField } }` or `masterDetail` |
| `@tanstack/react-virtual` / `useVirtualizer()` | Built-in row virtualization — no setup |
| `state.sorting` / `onSortingChange` | `sort-change` event |
| `state.rowSelection` / `onRowSelectionChange` | `features: { selection: 'row' }` + `selection-change` event |

```tsx
// TanStack Table (React)
import {
  useReactTable,
  getCoreRowModel,
  getSortedRowModel,
  flexRender,
} from '@tanstack/react-table';

const table = useReactTable({
  data: rows,
  columns,
  getCoreRowModel: getCoreRowModel(),
  getSortedRowModel: getSortedRowModel(),
});

// …then hand-render <table>, <thead>, <tbody> with flexRender(...)
```

```tsx
// Toolbox Grid (React)
import '@toolbox-web/grid-react/features/selection';
import { DataGrid, type GridConfig } from '@toolbox-web/grid-react';

const gridConfig: GridConfig<Row> = {
  columns, // { field, header, sortable, renderer? }
  features: { selection: 'row' },
};

<DataGrid rows={rows} gridConfig={gridConfig} style={{ height: 400 }} />;
```

### From ngx-datatable

ngx-datatable is Angular-only. Toolbox Grid's Angular adapter mirrors its declarative, template-driven style: keep `[rows]` as-is, rename column inputs, and swap `<ng-template>` cell templates for the `*tbwRenderer` structural directive.

| ngx-datatable | Toolbox Grid |
|---------------|--------------|
| `<ngx-datatable>` | `<tbw-grid>` with the `Grid` directive |
| `[rows]` | `[rows]` (same!) |
| `[columns]="[{ prop, name }]"` | `[columns]="[{ field, header }]"` |
| `<ngx-datatable-column prop="x">` | `<tbw-grid-column field="x">` |
| `[name]` | `header` |
| `<ng-template ngx-datatable-cell-template let-value="value">` | `*tbwRenderer="let value"` |
| `[sortable]` on column | `sortable: true` on column |
| `(activate)` | `(cellActivate)` / `cell-activate` event |
| `[selected]` / `(select)` + `SelectionType` | `[selection]="'row'"` + `selection-change` event |
| `[scrollbarV]="true"` (virtual scroll) | Built-in row virtualization — no input needed |
| `[groupRowsBy]` | `GroupingRowsPlugin` (`features: { groupingRows }`) |
| `[treeFromRelation]` / `[treeToRelation]` | `TreePlugin` (`features: { tree }`) |

```html
<!-- ngx-datatable -->
<ngx-datatable
  [rows]="rows"
  [columns]="[{ prop: 'name', name: 'Name' }, { prop: 'email', name: 'Email' }]"
  [selectionType]="'single'"
  (select)="onSelect($event)">
</ngx-datatable>
```

```html
<!-- Toolbox Grid (Angular adapter) -->
<!-- imports: [Grid, GridSelectionDirective] -->
<tbw-grid
  style="height: 400px;"
  [rows]="rows"
  [columns]="[{ field: 'name', header: 'Name' }, { field: 'email', header: 'Email' }]"
  [selection]="'row'"
  (selectionChange)="onSelect($event)">
</tbw-grid>
```

## Get Started

```bash
npm install @toolbox-web/grid
```

```typescript
import '@toolbox-web/grid';

const grid = document.createElement('tbw-grid');
grid.columns = [
  { field: 'name', header: 'Name', sortable: true },
  { field: 'email', header: 'Email' },
];
grid.rows = [
  { name: 'Alice', email: 'alice@example.com' },
  { name: 'Bob', email: 'bob@example.com' },
];
document.body.appendChild(grid);
```

**Ready to switch?** Check out our [Getting Started guide](/grid/getting-started.md) or [live demos](/grid/demos.md).

## See Also

- [Getting Started](/grid/getting-started.md) — Install and set up your first grid in minutes
- [Demos](/grid/demos.md) — Full-featured employee management demo
- [Plugins Overview](/grid/plugins.md) — Complete plugin catalog with feature comparison
