react-data-grid vs handsontable vs ag-grid
Data Grid Libraries Comparison
1 Year
react-data-gridhandsontableag-gridSimilar Packages:
What's Data Grid Libraries?

Data grid libraries are essential tools in web development that provide a structured way to display and manipulate tabular data. They come with a variety of features such as sorting, filtering, and editing capabilities, making it easier for developers to create interactive and user-friendly applications. These libraries are particularly useful in applications that require handling large datasets, allowing for efficient data management and visualization. Each library has its unique strengths and weaknesses, catering to different use cases and developer preferences.

Package Weekly Downloads Trend
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
react-data-grid202,0747,303402 kB6116 days agoMIT
handsontable152,54921,13320.8 MB4102 months agoSEE LICENSE IN LICENSE.txt
ag-grid11,38114,093-1337 years agoMIT
Feature Comparison: react-data-grid vs handsontable vs ag-grid

Customization

  • react-data-grid:

    React Data Grid allows for moderate customization through its API, enabling developers to create custom cell renderers and editors. However, it is less flexible compared to ag-Grid, making it more suitable for simpler use cases where extensive customization is not required.

  • handsontable:

    Handsontable provides a user-friendly API for customization, enabling developers to create custom cell types and editors. It also supports various themes and styles, making it easy to integrate into existing applications while maintaining a consistent look and feel.

  • ag-grid:

    ag-Grid offers extensive customization options, allowing developers to define custom cell renderers, editors, and filters. It supports themes and provides a rich API for fine-tuning the grid's behavior and appearance, making it suitable for complex applications that require tailored solutions.

Performance

  • react-data-grid:

    React Data Grid is designed for performance in React applications, utilizing techniques like virtualization to optimize rendering. It is a good choice for applications that require quick interactions with moderately sized datasets.

  • handsontable:

    Handsontable performs well with moderate datasets, but may experience performance issues with very large datasets due to its reliance on DOM manipulation for rendering. It is best suited for applications where data size is manageable and user interaction is prioritized.

  • ag-grid:

    ag-Grid is optimized for performance, capable of handling large datasets efficiently. It utilizes virtual scrolling and lazy loading techniques to ensure smooth rendering and interaction, making it ideal for applications with extensive data requirements.

Integration

  • react-data-grid:

    React Data Grid is specifically built for React applications, ensuring seamless integration with the React ecosystem. It leverages React's component-based architecture, making it a natural fit for developers already using React.

  • handsontable:

    Handsontable is primarily focused on providing a spreadsheet-like experience and integrates well with vanilla JavaScript and frameworks like React and Angular. Its API is straightforward, making it easy to implement in various projects.

  • ag-grid:

    ag-Grid can be integrated with various frameworks and libraries, including Angular, Vue, and React. Its flexibility allows it to fit into different tech stacks, making it a versatile choice for developers working across multiple platforms.

Features

  • react-data-grid:

    React Data Grid provides essential features such as sorting, filtering, and inline editing. While it may not have as many advanced features as ag-Grid, it strikes a balance between functionality and ease of use for typical data grid needs.

  • handsontable:

    Handsontable offers features like cell editing, data validation, and formula support, resembling a spreadsheet application. It is particularly useful for applications that require user-friendly data entry and manipulation capabilities.

  • ag-grid:

    ag-Grid is packed with features including grouping, aggregation, pivoting, filtering, and sorting. It also supports advanced functionalities like row grouping and master-detail views, making it suitable for complex data scenarios.

Learning Curve

  • react-data-grid:

    React Data Grid is designed to be user-friendly, with a gentle learning curve for React developers. Its simplicity and focus on core functionalities make it easy to get started without extensive setup.

  • handsontable:

    Handsontable is relatively easy to learn, especially for developers familiar with spreadsheet concepts. Its straightforward API allows for quick implementation, making it accessible for those new to data grid libraries.

  • ag-grid:

    ag-Grid has a steeper learning curve due to its extensive feature set and customization options. Developers may need to invest time in understanding its API and configurations to fully leverage its capabilities.

How to Choose: react-data-grid vs handsontable vs ag-grid
  • react-data-grid:

    Opt for React Data Grid if you want a lightweight and easy-to-use grid specifically designed for React applications. It offers a good balance of features and performance, making it suitable for projects that need quick implementation without extensive configuration.

  • handsontable:

    Select Handsontable if you are looking for a spreadsheet-like experience with a focus on data entry and manipulation. It is particularly useful for applications that require Excel-like functionalities such as cell editing, formulas, and data validation.

  • ag-grid:

    Choose ag-Grid if you need a highly customizable and feature-rich grid solution that supports complex data operations, including grouping, aggregation, and pivoting. It is ideal for enterprise-level applications where performance and flexibility are critical.

README for react-data-grid

react-data-grid

npm-badge type-badge size-badge codecov-badge ci-badge

The DataGrid component is designed to handle large datasets efficiently while offering a rich set of features for customization and interactivity.

Features

Links

Installation

Install react-data-grid using your favorite package manager:

npm i react-data-grid
pnpm add react-data-grid
yarn add react-data-grid
bun add react-data-grid

Additionally, import the default styles in your application:

import 'react-data-grid/lib/styles.css';

react-data-grid is published as ECMAScript modules for evergreen browsers, bundlers, and server-side rendering.

Getting started

Here is a basic example of how to use react-data-grid in your React application:

import 'react-data-grid/lib/styles.css';

import { DataGrid, type Column } from 'react-data-grid';

interface Row {
  id: number;
  title: string;
}

const columns: Column<Row>[] = [
  { key: 'id', name: 'ID' },
  { key: 'title', name: 'Title' }
];

const rows: Row[] = [
  { id: 0, title: 'Example' },
  { id: 1, title: 'Demo' }
];

function App() {
  return <DataGrid columns={columns} rows={rows} />;
}

API Reference

Components

<DataGrid />

DataGridProps
columns: readonly Column<R, SR>[]

See Column.

An array of column definitions. Each column should have a key and name.

:warning: Passing a new columns array will trigger a re-render for the whole grid, avoid changing it as much as possible for optimal performance.

rows: readonly R[]

An array of rows, the rows data can be of any type.

topSummaryRows?: Maybe<readonly SR[]>

Rows pinned at the top of the grid for summary purposes.

bottomSummaryRows?: Maybe<readonly SR[]>

Rows pinned at the bottom of the grid for summary purposes.

rowKeyGetter?: Maybe<(row: R) => K>

Function to return a unique key/identifier for each row. rowKeyGetter is required for row selection to work.

import { DataGrid } from 'react-data-grid';

interface Row {
  id: number;
  name: string;
}

function rowKeyGetter(row: Row) {
  return row.id;
}

function MyGrid() {
  return <DataGrid columns={columns} rows={rows} rowKeyGetter={rowKeyGetter} />;
}

:bulb: While optional, setting this prop is recommended for optimal performance as the returned value is used to set the key prop on the row elements.

onRowsChange?: Maybe<(rows: R[], data: RowsChangeData<R, SR>) => void>

Callback triggered when rows are changed.

The first parameter is a new rows array with both the updated rows and the other untouched rows. The second parameter is an object with an indexes array highlighting which rows have changed by their index, and the column where the change happened.

import { useState } from 'react';
import { DataGrid } from 'react-data-grid';

function MyGrid() {
  const [rows, setRows] = useState(initialRows);

  return <DataGrid columns={columns} rows={rows} onRowsChange={setRows} />;
}
rowHeight?: Maybe<number | ((row: R) => number)>

Default: 35 pixels

Height of each row in pixels. A function can be used to set different row heights.

headerRowHeight?: Maybe<number>

Default: 35 pixels

Height of the header row in pixels.

summaryRowHeight?: Maybe<number>

Default: 35 pixels

Height of each summary row in pixels.

columnWidths?: Maybe<ColumnWidths>

A map of column widths containing both measured and resized widths. If not provided then an internal state is used.

const [columnWidths, setColumnWidths] = useState((): ColumnWidths => new Map());

function addNewRow() {
  setRows(...);
  // reset column widths after adding a new row
  setColumnWidths(new Map());
}

return <DataGrid columnWidths={columnWidths} onColumnWidthsChange={setColumnWidths} ... />
onColumnWidthsChange?: Maybe<(columnWidths: ColumnWidths) => void>

Callback triggered when column widths change. If not provided then an internal state is used.

selectedRows?: Maybe<ReadonlySet<K>>

A set of selected row keys. rowKeyGetter is required for row selection to work.

isRowSelectionDisabled?: Maybe<(row: NoInfer<R>) => boolean>

Function to determine if row selection is disabled for a specific row.

onSelectedRowsChange?: Maybe<(selectedRows: Set<K>) => void>

Callback triggered when the selection changes.

import { useState } from 'react';
import { DataGrid, SelectColumn } from 'react-data-grid';

const rows: readonly Rows[] = [...];

const columns: readonly Column<Row>[] = [
  SelectColumn,
  // other columns
];

function MyGrid() {
  const [selectedRows, setSelectedRows] = useState((): ReadonlySet<number> => new Set());

  return (
    <DataGrid
      rowKeyGetter={rowKeyGetter}
      columns={columns}
      rows={rows}
      selectedRows={selectedRows}
      isRowSelectionDisabled={isRowSelectionDisabled}
      onSelectedRowsChange={setSelectedRows}
    />
  );
}

function rowKeyGetter(row: Row) {
  return row.id;
}

function isRowSelectionDisabled(row: Row) {
  return !row.isActive;
}
sortColumns?: Maybe<readonly SortColumn[]>

An array of sorted columns.

onSortColumnsChange?: Maybe<(sortColumns: SortColumn[]) => void>

Callback triggered when sorting changes.

import { useState } from 'react';
import { DataGrid, SelectColumn } from 'react-data-grid';

const rows: readonly Rows[] = [...];

const columns: readonly Column<Row>[] = [
  {
    key: 'name',
    name: 'Name',
    sortable: true
  },
  // other columns
];

function MyGrid() {
  const [sortColumns, setSortColumns] = useState<readonly SortColumn[]>([]);

  return (
    <DataGrid
      columns={columns}
      rows={rows}
      sortColumns={sortColumns}
      onSortColumnsChange={setSortColumns}
    />
  );
}

More than one column can be sorted via ctrl (command) + click. To disable multiple column sorting, change the onSortColumnsChange function to

function onSortColumnsChange(sortColumns: SortColumn[]) {
  setSortColumns(sortColumns.slice(-1));
}
defaultColumnOptions?: Maybe<DefaultColumnOptions<R, SR>>

Default options applied to all columns.

function MyGrid() {
  return (
    <DataGrid
      columns={columns}
      rows={rows}
      defaultColumnOptions={{
        minWidth: 100,
        resizable: true,
        sortable: true,
        draggable: true
      }}
    />
  );
}
onFill?: Maybe<(event: FillEvent<R>) => R>
onCellMouseDown: Maybe<(args: CellMouseArgs<R, SR>, event: CellMouseEvent) => void>

Callback triggered when a pointer becomes active in a cell. The default behavior is to select the cell. Call preventGridDefault to prevent the default behavior.

function onCellMouseDown(args: CellMouseDownArgs<R, SR>, event: CellMouseEvent) {
  if (args.column.key === 'id') {
    event.preventGridDefault();
  }
}

<DataGrid rows={rows} columns={columns} onCellMouseDown={onCellMouseDown} />;

See CellMouseArgs and CellMouseEvent

onCellClick?: Maybe<(args: CellMouseArgs<R, SR>, event: CellMouseEvent) => void>

Callback triggered when a cell is clicked.

function onCellClick(args: CellMouseArgs<R, SR>, event: CellMouseEvent) {
  if (args.column.key === 'id') {
    event.preventGridDefault();
  }
}

<DataGrid rows={rows} columns={columns} onCellClick={onCellClick} />;

This event can be used to open cell editor on single click

function onCellClick(args: CellMouseArgs<R, SR>, event: CellMouseEvent) {
  if (args.column.key === 'id') {
    args.selectCell(true);
  }
}

See CellMouseArgs and CellMouseEvent

onCellDoubleClick?: Maybe<(args: CellMouseArgs<R, SR>, event: CellMouseEvent) => void>

Callback triggered when a cell is double-clicked. The default behavior is to open the editor if the cell is editable. Call preventGridDefault to prevent the default behavior.

function onCellDoubleClick(args: CellMouseArgs<R, SR>, event: CellMouseEvent) {
  if (args.column.key === 'id') {
    event.preventGridDefault();
  }
}

<DataGrid rows={rows} columns={columns} onCellDoubleClick={onCellDoubleClick} />;

See CellMouseArgs and CellMouseEvent

onCellContextMenu?: Maybe<(args: CellMouseArgs<R, SR>, event: CellMouseEvent) => void>

Callback triggered when a cell is right-clicked.

function onCellContextMenu(args: CellMouseArgs<R, SR>, event: CellMouseEvent) {
  if (args.column.key === 'id') {
    event.preventDefault();
    // open custom context menu
  }
}

<DataGrid rows={rows} columns={columns} onCellContextMenu={onCellContextMenu} />;

See CellMouseArgs and CellMouseEvent

onCellKeyDown?: Maybe<(args: CellKeyDownArgs<R, SR>, event: CellKeyboardEvent) => void>

A function called when keydown event is triggered on a cell. This event can be used to customize cell navigation and editing behavior.

Examples

  • Prevent editing on Enter
function onCellKeyDown(args: CellKeyDownArgs<R, SR>, event: CellKeyboardEvent) {
  if (args.mode === 'SELECT' && event.key === 'Enter') {
    event.preventGridDefault();
  }
}
  • Prevent navigation on Tab
function onCellKeyDown(args: CellKeyDownArgs<R, SR>, event: CellKeyboardEvent) {
  if (args.mode === 'SELECT' && event.key === 'Tab') {
    event.preventGridDefault();
  }
}

Check more examples

onCellCopy?: Maybe<(args: CellCopyArgs<NoInfer<R>, NoInfer<SR>>, event: CellClipboardEvent) => void>

Callback triggered when a cell's content is copied.

onCellPaste?: Maybe<(args: CellPasteArgs<NoInfer<R>, NoInfer<SR>>, event: CellClipboardEvent) => void>

Callback triggered when content is pasted into a cell.

onSelectedCellChange?: Maybe<(args: CellSelectArgs<R, SR>) => void>;

Triggered when the selected cell is changed.

Arguments:

  • args.rowIdx: number - row index
  • args.row: R - row object of the currently selected cell
  • args.column: CalculatedColumn<TRow, TSummaryRow> - column object of the currently selected cell
onScroll?: Maybe<(event: React.UIEvent<HTMLDivElement>) => void>

Callback triggered when the grid is scrolled.

onColumnResize?: Maybe<(column: CalculatedColumn<R, SR>, width: number) => void>

Callback triggered when column is resized.

onColumnsReorder?: Maybe<(sourceColumnKey: string, targetColumnKey: string) => void>

Callback triggered when columns are reordered.

enableVirtualization?: Maybe<boolean>

Default: true

This prop can be used to disable virtualization.

renderers?: Maybe<Renderers<R, SR>>

Custom renderers for cells, rows, and other components.

interface Renderers<TRow, TSummaryRow> {
  renderCell?: Maybe<(key: Key, props: CellRendererProps<TRow, TSummaryRow>) => ReactNode>;
  renderCheckbox?: Maybe<(props: RenderCheckboxProps) => ReactNode>;
  renderRow?: Maybe<(key: Key, props: RenderRowProps<TRow, TSummaryRow>) => ReactNode>;
  renderSortStatus?: Maybe<(props: RenderSortStatusProps) => ReactNode>;
  noRowsFallback?: Maybe<ReactNode>;
}

For example, the default <Row /> component can be wrapped via the renderRow prop to add contexts or tweak props

import { DataGrid, RenderRowProps, Row } from 'react-data-grid';

function myRowRenderer(key: React.Key, props: RenderRowProps<Row>) {
  return (
    <MyContext key={key} value={123}>
      <Row {...props} />
    </MyContext>
  );
}

function MyGrid() {
  return <DataGrid columns={columns} rows={rows} renderers={{ renderRow: myRowRenderer }} />;
}

:warning: To prevent all rows from being unmounted on re-renders, make sure to pass a static or memoized render function to renderRow.

rowClass?: Maybe<(row: R, rowIdx: number) => Maybe<string>>

Function to apply custom class names to rows.

import { DataGrid } from 'react-data-grid';

function MyGrid() {
  return <DataGrid columns={columns} rows={rows} rowClass={rowClass} />;
}

function rowClass(row: Row, rowIdx: number) {
  return rowIdx % 2 === 0 ? 'even' : 'odd';
}
headerRowClass?: Maybe<string>>

Custom class name for the header row.

direction?: Maybe<'ltr' | 'rtl'>

This property sets the text direction of the grid, it defaults to 'ltr' (left-to-right). Setting direction to 'rtl' has the following effects:

  • Columns flow from right to left
  • Frozen columns are pinned on the right
  • Column resize cursor is shown on the left edge of the column
  • Scrollbar is moved to the left
className?: string | undefined

Custom class name for the grid.

style?: CSSProperties | undefined

Custom styles for the grid.

'aria-label'?: string | undefined

The label of the grid. We recommend providing a label using aria-label or aria-labelledby

'aria-labelledby'?: string | undefined

The id of the element containing a label for the grid. We recommend providing a label using aria-label or aria-labelledby

'aria-description'?: string | undefined
'aria-describedby'?: string | undefined

If the grid has a caption or description, aria-describedby can be set on the grid element with a value referring to the element containing the description.

'data-testid'?: Maybe<string>

This prop can be used to add a testid for testing. We recommend querying the grid by by its role and name.

function MyGrid() {
  return <DataGrid aria-label="my-grid" columns={columns} rows={rows} />;
}

test('grid', () => {
  render(<MyGrid />);
  const grid = screen.getByRole('grid', { name: 'my-grid' });
});

<TreeDataGrid />

TreeDataGrid is component built on top of DataGrid to add row grouping. This implements the Treegrid pattern. At the moment TreeDataGrid does not support onFill and isRowSelectionDisabled props

TreeDataGridProps
groupBy?: Maybe<readonly string[]>
rowGrouper?: Maybe<(rows: readonly R[], columnKey: string) => Record<string, readonly R[]>>
expandedGroupIds?: Maybe<ReadonlySet<unknown>>
onExpandedGroupIdsChange?: Maybe<(expandedGroupIds: Set<unknown>) => void>
groupIdGetter?: Maybe<(groupKey: string, parentId?: string) => string>

<TextEditor />

Props

See RenderEditCellProps

<Row />

See renderers

Props

See RenderRowProps

The ref prop is supported.

<SortableHeaderCell />

Props
onSort: (ctrlClick: boolean) => void
sortDirection: SortDirection | undefined
priority: number | undefined
tabIndex: number
children: React.ReactNode

<ValueFormatter />

Props

See FormatterProps

<SelectCellFormatter />

Props
value: boolean
tabIndex: number
disabled?: boolean | undefined
onChange: (value: boolean, isShiftClick: boolean) => void
onClick?: MouseEventHandler<T> | undefined
'aria-label'?: string | undefined
'aria-labelledby'?: string | undefined

<ToggleGroupFormatter />

Props

See RenderGroupCellProps

Hooks

useHeaderRowSelection<R>(): { isIndeterminate, isRowSelected, onRowSelectionChange }

useRowSelection<R>(): { isRowSelectionDisabled, isRowSelected, onRowSelectionChange }

Other

SelectColumn: Column<any, any>

SELECT_COLUMN_KEY = 'rdg-select-column'

Types

Column

name: string | ReactElement

The name of the column. Displayed in the header cell by default.

key: string

A unique key to distinguish each column

width?: Maybe<number | string>

Default auto

Width can be any valid css grid column value. If not specified, it will be determined automatically based on grid width and specified widths of other columns.

width: 80, // pixels
width: '25%',
width: 'max-content',
width: 'minmax(100px, max-content)',

max-content can be used to expand the column to show all the content. Note that the grid is only able to calculate column width for visible rows.

minWidth?: Maybe<number>

Default: 50 pixels

Minimum column width in pixels.

maxWidth?: Maybe<number>

Maximum column width in pixels.

cellClass?: Maybe<string | ((row: TRow) => Maybe<string>)>

Class name(s) for the cell

headerCellClass?: Maybe<string>

Class name(s) for the header cell.

summaryCellClass?: Maybe<string | ((row: TSummaryRow) => Maybe<string>)>

Class name(s) for the summary cell.

renderCell?: Maybe<(props: RenderCellProps<TRow, TSummaryRow>) => ReactNode>

Render function to render the content of cells.

renderHeaderCell?: Maybe<(props: RenderHeaderCellProps<TRow, TSummaryRow>) => ReactNode>

Render function to render the content of the header cell.

renderSummaryCell?: Maybe<(props: RenderSummaryCellProps<TSummaryRow, TRow>) => ReactNode>

Render function to render the content of summary cells

renderEditCell?: Maybe<(props: RenderEditCellProps<TRow, TSummaryRow>) => ReactNode>

Render function to render the content of edit cells. When set, the column is automatically set to be editable

editable?: Maybe<boolean | ((row: TRow) => boolean)>

Enables cell editing. If set and no editor property specified, then a text input will be used as the cell editor.

colSpan?: Maybe<(args: ColSpanArgs<TRow, TSummaryRow>) => Maybe<number>>
frozen?: Maybe<boolean>

Default: false

Determines whether column is frozen. Frozen columns are pinned on the left. At the moment we do not support pinning columns on the right.

resizable?: Maybe<boolean>

Default: false

Enable resizing of the column

sortable?: Maybe<boolean>

Default: false

Enable sorting of the column

draggable?: Maybe<boolean>

Default: false

Enable dragging of the column

sortDescendingFirst?: Maybe<boolean>

Default: false

Sets the column sort order to be descending instead of ascending the first time the column is sorted

editorOptions

Options for cell editing.

displayCellContent?: Maybe<boolean>

Default: false

Render the cell content in addition to the edit cell. Enable this option when the editor is rendered outside the grid, like a modal for example.

commitOnOutsideClick?: Maybe<boolean>

Default: true

Commit changes when clicking outside the cell.

closeOnExternalRowChange?: Maybe<boolean>

Default: true

Close the editor when the row changes externally.

CellMouseArgs

rowIdx: number

Row index of the currently selected cell

row: TRow

row object of the currently selected cell

column: CalculatedColumn<TRow, TSummaryRow>

column object of the currently selected cell

selectCell: (enableEditor?: boolean) => void

function to manually select the cell and optionally pass true to start editing

CellMouseEvent

Extends React.MouseEvent<HTMLDivElement>

event.preventGridDefault: () => void
event.isGridDefaultPrevented: boolean

DataGridHandle

RenderEditCellProps

RenderCellProps

RenderGroupCellProps

RenderRowProps

Generics

  • R, TRow: Row type
  • SR, TSummaryRow: Summary row type
  • K: Row key type