A data grid that stays fast at a million rows
Virtualized rows and columns, sorting and filtering in a Web Worker, and 50 of 82 features that need no licence key. For React 18+ and for plain JavaScript, from one engine.
Sort it, filter it, group it — nothing here is a screenshot
One install, no bundler configuration
npm install @kanunilabs/datagrid-reactimport { DataGrid } from '@kanunilabs/datagrid-react';
import '@kanunilabs/datagrid-react/styles.css';
const columns = [
{ field: 'id', headerName: 'ID', dataType: 'number', width: 80, pin: 'left' },
{ field: 'product', headerName: 'Product', width: 220 },
{ field: 'revenue', headerName: 'Revenue', dataType: 'number' },
];
<DataGrid
gridId="sales"
dataSource={rows}
rowKey="id"
columns={columns}
sorting={{ multi: true }}
filterRow
toolbar
style={{ height: 600 }}
/>No framework? createGrid() renders the same grid into a plain element, with a UMD build for a bare <script> tag.
Measured, with the method published
- Sort, numeric
- 90 ms
- Sort, text
- 108 ms
- Sort, two columns
- 192 ms
- Filter, equality
- 19 ms
- Filter, 3-clause AND
- 63 ms
- Free-text search
- 58 ms
1,000,000 rows × 15 columns, worker path, median of 5 runs. The method, the caveats, and the command to run it yourself. We publish no comparison against other grids — a benchmark we ran on someone else's library would not be evidence, it would be marketing.
Every feature, and which edition it needs
Read from the inventory the product itself uses, so this list cannot disagree with the grid you install.
Data & performance
- Virtual scrollingCommunityA million rows at 60fps — only the visible window is in the DOM.
- Web Worker offloadCommunityFiltering and sorting move off the main thread above ~20,000 rows.
- Columnar encodingCommunityTyped arrays instead of objects — the reason sort and filter stay fast.
- Server-side dataEnterpriseFiltering, sorting, paging, distinct values and totals run on your server.
- PaginationCommunityClassic pager with page size control.
- Infinite scrollCommunityAppend rows as the user reaches the end.
- TransactionsCommunityAdd, update and remove rows without rebuilding the grid.
- Async transactionsCommunityBatch a burst of live updates into one pass.
Columns
- SortingCommunitySingle and multi-column, with a custom comparator escape hatch.
- ResizingCommunityDrag the edge; double-click for best fit.
- ReorderingCommunityDrag a header to move the column.
- PinningCommunityFreeze columns to either edge.
- Visibility & chooserCommunityHide columns and bring them back from a picker.
- Bands (multi-level headers)CommunityGroup headers over related columns, collapsible.
- Auto-size & fitCommunitySize to content or fill the grid exactly.
- Automatic columnsCommunityInfer columns from the first row when you have not declared any.
- Custom sort orderCommunitySort by your own rule — "Critical > High > Normal".
- Cell span / mergeEnterpriseMerge repeating cells down a column.
Filtering
- Filter rowCommunityA per-column input under the header.
- Header filter popupCommunityCheckbox list of every distinct value, virtualized and unlimited.
- Quick searchCommunityOne box across every column, with match highlighting.
- Filter panel barCommunityShows the active filter as text, with one-click clear.
- External filterCommunityAND your own row predicate into the grid filter.
- Multi filterCommunityThe filter row and the popup coexist instead of overwriting each other.
- Filter builderEnterpriseNested AND/OR conditions, built visually.
Grouping & hierarchy
- Grouping (one level)CommunityGroup by a column; drag it to the group panel.
- Group panelCommunityDrag columns in and out to regroup.
- Multi-level groupingEnterpriseNest groups as deep as the data goes.
- Group summariesEnterprisePer-group totals in the group footer.
- Master-detailEnterpriseExpand a row into your own detail panel.
- Tree dataEnterpriseSelf-referencing hierarchies with expand/collapse.
Totals
- Footer column totalsCommunityColumn-aligned totals over the whole filtered result.
- Totals row placementCommunityPin the totals row to the top or the bottom.
- Custom aggregationCommunityYour own reducer beside sum/avg/min/max/count.
- Selection statisticsEnterpriseSum, average, min, max and count of the selected range.
Selection & keyboard
- Row selectionCommunitySingle, multiple, and checkbox selection.
- Cell focus & keyboardCommunityArrow keys, Tab, Home/End, Page Up/Down.
- Focused rowCommunityA highlighted current row, independent of selection.
- Deferred selectionCommunityApply the selection when the user confirms it.
- CopyCommunityCtrl+C, with or without headers.
- Range selectionEnterpriseDrag a rectangle across cells.
- Fill handleEnterpriseDrag the corner to fill a range, spreadsheet-style.
- Cut & pasteEnterprisePaste a block from Excel straight into a range.
Editing
- Cell editingEnterpriseEdit one cell at a time, committed on blur or Enter.
- Batch editingEnterpriseCollect changes and save them together.
- Row editingEnterpriseEdit a whole row with save/cancel controls.
- Form editingEnterpriseEdit the record as a form laid out in the row flow.
- Popup editingEnterpriseEdit the record in a dialog over the grid.
- ValidationEnterprisePer-column rules with inline error reporting.
- CRUD handlersEnterpriseOne call per row — the shape REST and SQL want.
- PermissionsEnterpriseAllow add, update and delete per row, enforced in the engine.
- Custom editors & lookupsEnterpriseYour own editor component, or a lookup bound to another source.
- Undo / redoEnterpriseFull history with a public API and events.
Export & import
- CSV exportCommunityThe whole result or the current selection.
- Excel exportEnterpriseReal .xlsx, with the grid’s formatting carried over.
- PDF exportEnterprisePaginated, with repeated headers; streams for large results.
- Styled exportEnterpriseNumber formats, banded rows, outline levels, totals.
- Import wizardEnterpriseFile → column mapping → commit.
Presentation
- Themes & palettesCommunitySix themes, six palettes, light and dark.
- DensityCommunityNormal and compact row heights.
- LocalizationCommunityTwelve languages, and your own strings.
- TooltipsCommunityAutomatic on truncated text, or your own content.
- Row numbersCommunityA numbered column that renumbers after sort and filter.
- Print layoutCommunityEvery row on paper, not just the rendered window.
- Aligned gridsCommunityTwo grids sharing one column layout.
- Cell flashCommunityHighlight cells that just changed.
- Loading & empty overlaysCommunityBuilt-in states you can drive yourself.
- Pinned rowsCommunitySticky comparison rows above or below the data.
- Row drag & dropCommunityReorder rows, or move them between grids.
- SparklinesEnterpriseA small chart inside a cell.
- Cell notesEnterpriseA corner marker with text on hover, editable from the menu.
- Status barEnterpriseDocked counters and range statistics.
- Side bar & tool panelsEnterpriseDocked Columns and Filters panels.
Charts
- Chart data hookCommunityShapes the grid’s current result for any chart library.
- Chart panelEnterpriseDocked charts driven by the grid’s own columns.
- Cross-filter & zoomEnterpriseClick a bar to filter the grid; wheel to zoom the axis.
- Chart exportEnterpriseSave a chart as PNG or SVG.
Integration
- EventsCommunityFifty-five events covering cells, rows, columns, editing and lifecycle.
- Imperative APICommunityDrive the grid from your own code via the controller.
- Saved viewsCommunityPersist and restore column, filter and sort state.
- Context menusCommunityHeader, row and cell menus you can extend or replace.
- SSR & bundlersCommunityRenders on the server; works with Vite, Next.js and webpack.
Before you read the guide
How many rows can it actually handle?
The playground lets you push the demo to 1,000,000 rows in your own browser — that is the honest test, and it is one click away. Above ~20,000 rows sorting and filtering move into a Web Worker automatically, so the main thread stays free for your UI.
Does it work without React?
Yes, with full feature parity. @kanunilabs/datagrid builds the grid straight into the DOM with one createGrid(element, config) call, and a UMD build means a script tag is a complete install. The parity is enforced in CI, not asserted in a README.
Angular or Vue?
No. React and plain JavaScript only. If your stack is Angular or Vue this is the wrong component today, and knowing that now is worth more than discovering it after an afternoon.
What do I have to configure for the Web Worker?
Nothing. It is compiled into the package and started from a Blob URL — no file to copy into public/, no bundler recipe, no import. Under a CSP that forbids blob: workers you can point at a served file instead.
Is the free edition crippled?
No watermark, no expiry, no signup, and commercial use is allowed. What the paid edition adds is a coherent boundary rather than an arbitrary one: changing the data (editing, validation, undo, range and fill) and taking it out (styled Excel and PDF, charts, server-side data).
Can I see the source?
No — it is proprietary, free of charge but not open source. If an OSI licence is a hard requirement, this is not your library.
One licence covers this and PivotGrid
A paid key unlocks the Enterprise features of both components — there is no second purchase. 2,401 tests stand behind them.