The DataGrid component is designed to handle large datasets efficiently while offering a rich set of features for customization and interactivity.
- React 19.2+ support
- Evergreen browsers and server-side rendering support
- Tree-shaking support with no external dependencies to keep your bundles slim
- Great performance thanks to virtualization: columns and rows outside the viewport are not rendered
- Strictly typed with TypeScript
- Keyboard accessibility
- Light and dark mode support out of the box via
color-scheme - Frozen columns: Freeze columns to keep them visible during horizontal scrolling.
- Column resizing
- Multi-column sorting
- Click on a sortable column header to toggle between its ascending/descending sort order
- Ctrl+Click / Meta+Click to sort an additional column
- Column spanning
- Column grouping
- Row selection
- Row grouping
- Summary rows
- Dynamic row heights
- No rows fallback
- Cell formatting
- Cell editing
- Cell copying / pasting
- Cell value dragging / filling
- Customizable renderers
- Right-to-left (RTL) support
Install react-data-grid using your favorite package manager:
# npm
npm install react-data-grid
# Vite+
vp add react-data-grid
# pnpm
pnpm add react-data-grid
# Yarn
yarn add react-data-grid
# Bun
bun add react-data-gridAdditionally, 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.
Important
Vite 8+ uses Lightning CSS by default to minify CSS, and Lightning CSS has a bug minifying thelight-dark()syntax. You can tweak thecssMinifyorcssTargetsettings as a workaround.
build: {
cssMinify: 'esbuild', // requires the `esbuild` package to be installed
// or
cssTarget: 'esnext'
}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: readonly Column<Row>[] = [
{ key: 'id', name: 'ID' },
{ key: 'title', name: 'Title' }
];
const rows: readonly Row[] = [
{ id: 0, title: 'Example' },
{ id: 1, title: 'Demo' }
];
function App() {
return <DataGrid columns={columns} rows={rows} />;
}The DataGrid provides multiple ways to customize its appearance and behavior.
The DataGrid supports both light and dark color schemes out of the box using the light-dark() CSS function. The theme automatically adapts based on the user's system preference when color-scheme: light dark; is set.
To enforce a specific theme, we recommend setting the standard color-scheme CSS property on the :root:
:root {
color-scheme: light; /* or 'dark', or 'light dark' for auto */
}Alternatively, you can add the rdg-light or rdg-dark class to individual grids:
// Force light theme
<DataGrid className="rdg-light" columns={columns} rows={rows} />
// Force dark theme
<DataGrid className="rdg-dark" columns={columns} rows={rows} />The DataGrid supports the following CSS variables for customization:
.rdg {
/* Selection */
--rdg-selection-width: 2px;
--rdg-selection-color: hsl(207, 75%, 66%);
/* Typography */
--rdg-font-size: 14px;
/* Colors (using light-dark() for automatic theme switching) */
--rdg-color: light-dark(#000, #ddd);
--rdg-background-color: light-dark(hsl(0deg 0% 100%), hsl(0deg 0% 13%));
/* Header */
--rdg-header-background-color: light-dark(hsl(0deg 0% 97.5%), hsl(0deg 0% 10.5%));
--rdg-header-draggable-background-color: light-dark(hsl(0deg 0% 90.5%), hsl(0deg 0% 17.5%));
/* Rows */
--rdg-row-hover-background-color: light-dark(hsl(0deg 0% 96%), hsl(0deg 0% 9%));
--rdg-row-selected-background-color: light-dark(hsl(207deg 76% 92%), hsl(207deg 76% 42%));
--rdg-row-selected-hover-background-color: light-dark(hsl(207deg 76% 88%), hsl(207deg 76% 38%));
/* Borders */
--rdg-border-width: 1px;
--rdg-border-color: light-dark(#ddd, #444);
--rdg-summary-border-width: calc(var(--rdg-border-width) * 2);
--rdg-summary-border-color: light-dark(#aaa, #555);
/* Checkboxes */
--rdg-checkbox-focus-color: hsl(207deg 100% 69%);
/* Frozen columns */
--rdg-frozen-column-shadow-color: light-dark(rgb(0 0 0 / 15%), rgb(0 0 0 / 40%));
}Example of customizing colors:
.my-custom-grid {
--rdg-background-color: #f0f0f0;
--rdg-selection-color: #ff6b6b;
--rdg-font-size: 16px;
}<DataGrid className="my-custom-grid" columns={columns} rows={rows} />The DataGrid accepts standard className and style props:
<DataGrid
columns={columns}
rows={rows}
className="my-grid custom-theme"
style={{ width: 800, height: 600 }}
/>Control row heights using the rowHeight, headerRowHeight, and summaryRowHeight props. The rowHeight prop supports both fixed heights and dynamic heights per row.
Apply custom CSS classes to rows using the rowClass prop, and to header rows using the headerRowClass prop.
Apply custom CSS classes to cells using the cellClass property in column definitions. You can also use headerCellClass and summaryCellClass for header and summary cells, respectively.
Control column widths using the width, minWidth, and maxWidth properties in column definitions. Enable column resizing using the resizable property, or use defaultColumnOptions to apply it to all columns.
Replace default components with custom implementations using the renderers prop. Columns can also have custom renderers using the renderCell, renderHeaderCell, renderSummaryCell, renderGroupCell, and renderEditCell properties.
An array of column definitions and/or column groups. See the ColumnOrColumnGroup type for all available options.
columns array will trigger a re-render and recalculation for the entire grid. Always memoize this prop using useMemo or define it outside the component to avoid unnecessary re-renders.
An array of rows. The row data can be of any type.
💡 Performance: The grid is optimized for efficient rendering:
-
Virtualization: Only visible rows are rendered in the DOM
-
Individual row updates: Row components are memoized, so updating a single row object will only re-render that specific row, not all rows
-
Array reference matters: Changing the array reference itself (e.g.,
setRows([...rows])) triggers viewport and layout recalculations, even if the row objects are unchanged -
Best practice: When updating rows, create a new array but reuse unchanged row objects. For example:
// ✅ Good: Only the changed row is re-rendered setRows(rows.map((row, idx) => (idx === targetIdx ? { ...row, updated: true } : row))); // ❌ Avoid: Creates new references for all rows, causing all visible rows to re-render setRows(rows.map((row) => ({ ...row })));
Optional ref for imperative APIs like scrolling to or focusing a cell. See DataGridHandle.
Rows pinned at the top of the grid for summary purposes.
Rows pinned at the bottom of the grid for summary purposes.
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} />;
}💡 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.
useCallback to prevent unnecessary re-renders.
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} />;
}Default: 35 pixels
Height of each row in pixels. A function can be used to set different row heights.
// Fixed height for all rows
<DataGrid columns={columns} rows={rows} rowHeight={50} />;
// Dynamic height per row
function getRowHeight(row) {
return row.isExpanded ? 100 : 35;
}
<DataGrid columns={columns} rows={rows} rowHeight={getRowHeight} />;rowHeight function.
Default: rowHeight when it is a number, otherwise 35 pixels
Height of the header row in pixels.
Default: rowHeight when it is a number, otherwise 35 pixels
Height of each summary row in pixels.
<DataGrid
columns={columns}
rows={rows}
rowHeight={35}
headerRowHeight={45}
summaryRowHeight={40}
topSummaryRows={topSummaryRows}
/>A map of column widths containing both measured and resized widths. If not provided, 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} ... />Callback triggered when column widths change. If not provided, an internal state is used.
A set of selected row keys. rowKeyGetter is required for row selection to work.
Function to determine if row selection is disabled for a specific row.
Callback triggered when the selection changes.
import { useState } from 'react';
import { DataGrid, SelectColumn, type Column } from 'react-data-grid';
const rows: readonly Row[] = [...];
const columns: readonly Column<Row>[] = [
SelectColumn,
// other columns
];
function rowKeyGetter(row: Row) {
return row.id;
}
function isRowSelectionDisabled(row: Row) {
return !row.isActive;
}
function MyGrid() {
const [selectedRows, setSelectedRows] = useState((): ReadonlySet<number> => new Set());
return (
<DataGrid
rowKeyGetter={rowKeyGetter}
columns={columns}
rows={rows}
selectedRows={selectedRows}
isRowSelectionDisabled={isRowSelectionDisabled}
onSelectedRowsChange={setSelectedRows}
/>
);
}An array of sorted columns.
Sorting is controlled: the grid does not reorder rows for you. Apply the sorting to your rows state (or derived rows) based on sortColumns.
Callback triggered when sorting changes.
import { useState } from 'react';
import { DataGrid, type Column, type SortColumn } from 'react-data-grid';
const rows: readonly Row[] = [...];
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+Click (Cmd+Click on macOS). To disable multi-column sorting, change the onSortColumnsChange function to:
function onSortColumnsChange(sortColumns: SortColumn[]) {
setSortColumns(sortColumns.slice(-1));
}Default options applied to all columns.
function MyGrid() {
return (
<DataGrid
columns={columns}
rows={rows}
defaultColumnOptions={{
minWidth: 100,
resizable: true,
sortable: true,
draggable: true
}}
/>
);
}Callback triggered when a pointer becomes active in a cell. The default behavior is to focus the cell. Call preventGridDefault to prevent the default behavior.
function onCellMouseDown(args: CellMouseArgs<R, SR>, event: CellMouseEvent) {
if (args.column.key === 'id') {
event.preventGridDefault();
}
}
<DataGrid rows={rows} columns={columns} onCellMouseDown={onCellMouseDown} />;Callback triggered when a cell is clicked.
This event can be used to open the cell editor on a single click:
function onCellClick(args: CellMouseArgs<R, SR>, event: CellMouseEvent) {
if (args.column.key === 'id') {
args.setActivePosition(true);
}
}
<DataGrid rows={rows} columns={columns} onCellClick={onCellClick} />;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} />;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} />;Callback triggered when a key is pressed in 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 === 'ACTIVE' && event.key === 'Enter') {
event.preventGridDefault();
}
}- Prevent navigation on
Tab
function onCellKeyDown(args: CellKeyDownArgs<R, SR>, event: CellKeyboardEvent) {
if (args.mode === 'ACTIVE' && event.key === 'Tab') {
event.preventGridDefault();
}
}See more examples.
onCellCopy?: Maybe<(args: CellCopyArgs<NoInfer<R>, NoInfer<SR>>, event: CellClipboardEvent) => void>
Callback triggered when a cell's content is copied.
Callback triggered when content is pasted into a cell.
Return the updated row; the grid will call onRowsChange with it.
Callback triggered when the active position changes.
See the PositionChangeArgs type in the Types section below.
Callback triggered when the value of the active cell is dragged to fill other cells. When this prop is set, a drag handle is shown on the active cell if it is editable. Drag the handle up or down to fill the cells it passes over, or double-click it to fill all the cells below.
The grid calls onFill for each editable target cell. Return the updated target row; the grid will call onRowsChange with the updated rows. See the FillEvent type.
function onFill({ columnKey, sourceRow, targetRow }: FillEvent<Row>): Row {
return { ...targetRow, [columnKey]: sourceRow[columnKey as keyof Row] };
}
<DataGrid columns={columns} rows={rows} onRowsChange={setRows} onFill={onFill} />;Native DOM onScroll prop.
Callback triggered when a column is resized.
Callback triggered when columns are reordered.
Default: true
This prop can be used to disable virtualization.
Custom renderers for cells, rows, and other components.
See the Renderers type for the full shape.
Example of replacing default components:
import { DataGrid, type Renderers } from 'react-data-grid';
const customRenderers: Renderers<Row, SummaryRow> = {
// Custom row render function
renderRow(key, props) {
return <CustomRow key={key} {...props} />;
},
// Custom cell render function
renderCell(key, props) {
return <CustomCell key={key} {...props} />;
},
// Custom checkbox render function
renderCheckbox(props) {
return <CustomCheckbox {...props} />;
},
// Custom sort status indicator
renderSortStatus(props) {
return <CustomSortIcon {...props} />;
},
// Custom empty state
noRowsFallback: <div>No data available</div>
};
<DataGrid columns={columns} rows={rows} renderers={customRenderers} />;The default <Row /> component can be wrapped via the renderRow prop to add contexts or tweak props:
import { DataGrid, Row, type RenderRowProps } from 'react-data-grid';
interface MyRow {
id: number;
}
function myRowRenderer(key: React.Key, props: RenderRowProps<MyRow>) {
return (
<MyContext key={key} value={123}>
<Row {...props} />
</MyContext>
);
}
function MyGrid() {
return <DataGrid columns={columns} rows={rows} renderers={{ renderRow: myRowRenderer }} />;
}Function to apply custom class names to rows.
import { DataGrid } from 'react-data-grid';
function rowClass(row: Row, rowIdx: number) {
return rowIdx % 2 === 0 ? 'even' : 'odd';
}
function MyGrid() {
return <DataGrid columns={columns} rows={rows} rowClass={rowClass} />;
}useCallback to avoid re-rendering all rows on every render.
Custom class name for the header row.
<DataGrid columns={columns} rows={rows} headerRowClass="sticky-header" />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
- Start-frozen columns are pinned on the right, and end-frozen columns are pinned on the left
- Column resize cursor is shown on the left edge of the column
- Scrollbar is moved to the left
Custom class name for the grid.
Custom styles for the grid.
ARIA role for the grid container. Defaults to grid.
The label of the grid. We recommend providing a label using aria-label or aria-labelledby.
The id of the element containing a label for the grid. We recommend providing a label using aria-label or aria-labelledby.
Total number of rows for assistive technologies.
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.
This prop can be used to add a test ID for testing. We recommend querying the grid by its role and name.
function MyGrid() {
return <DataGrid aria-label="my-grid" columns={columns} rows={rows} />;
}
test('grid', async () => {
await page.render(<MyGrid />);
const grid = page.getByRole('grid', { name: 'my-grid' });
});Optional attribute to help with Cypress (or similar) selectors.
TreeDataGrid is a component built on top of DataGrid to add hierarchical row grouping. This implements the Treegrid pattern.
How it works:
- The
groupByprop specifies which columns should be used for grouping - The
rowGrouperfunction groups rows by the specified column keys - Group rows are rendered with expand/collapse toggles
- Child rows are nested under their parent groups
- Groups can be expanded/collapsed by clicking the toggle or using keyboard navigation (←, →)
Keyboard Navigation:
- → (Right Arrow): Expand a collapsed group row when focused
- ← (Left Arrow): Collapse an expanded group row when focused, or navigate to the parent group
Unsupported Props:
The following DataGrid props are not supported in TreeDataGrid:
onFill- Drag-fill is disabled for tree gridsisRowSelectionDisabled- Row selection disabling is not availablerole-TreeDataGridmanages the ARIA rolearia-rowcount-TreeDataGridmanages the ARIA row count
Caveats:
- Group columns cannot be rendered under one column
- Group columns are automatically frozen and cannot be unfrozen
- Cell copy/paste does not work on group rows
- Column groups are not supported;
columnsmust beColumn[]
All DataGridProps are supported except those listed above. The columns prop only supports Column[] (no column groups). When rowHeight is a function, it receives RowHeightArgs instead of just the row. Additional props are listed below:
Required. An array of column keys to group by. The order determines the grouping hierarchy (first key is the top level, second key is nested under the first, etc.).
import { TreeDataGrid, type Column } from 'react-data-grid';
interface Row {
id: number;
country: string;
city: string;
name: string;
}
const columns: readonly Column<Row>[] = [
{ key: 'country', name: 'Country' },
{ key: 'city', name: 'City' },
{ key: 'name', name: 'Name' }
];
function MyGrid() {
return (
<TreeDataGrid
columns={columns}
rows={rows}
groupBy={['country', 'city']}
// ... other props
/>
);
}Required. A function that groups rows by the specified column key. Returns an object where keys are the group values and values are arrays of rows belonging to that group.
function rowGrouper(rows: readonly Row[], columnKey: string): Record<string, readonly Row[]> {
return Object.groupBy(rows, (row) => row[columnKey]);
}Required. A set of group IDs that are currently expanded. Group IDs are generated by groupIdGetter.
import { useState } from 'react';
import { TreeDataGrid } from 'react-data-grid';
function MyGrid() {
const [expandedGroupIds, setExpandedGroupIds] = useState((): ReadonlySet<unknown> => new Set());
return (
<TreeDataGrid
expandedGroupIds={expandedGroupIds}
onExpandedGroupIdsChange={setExpandedGroupIds}
// ... other props
/>
);
}Required. Callback triggered when groups are expanded or collapsed.
Function to generate unique IDs for group rows. If not provided, a default implementation is used that concatenates parent and group keys with __.
Note: Unlike DataGrid, the rowHeight function receives RowHeightArgs<R>, which includes a type property to distinguish between regular rows and group rows:
function getRowHeight(args: RowHeightArgs<Row>): number {
if (args.type === 'GROUP') {
return 50; // Custom height for group rows
}
return 35; // Height for regular rows
}
<TreeDataGrid rowHeight={getRowHeight} ... />The default row component. Can be wrapped via the renderers.renderRow prop.
RenderRowProps<TRow, TSummaryRow>
The default cell component. Can be wrapped via the renderers.renderCell prop.
CellRendererProps<TRow, TSummaryRow>
A formatter component for rendering row selection checkboxes.
The same props as RenderCheckboxProps, with value: boolean in place of checked.
Low-level component used by renderToggleGroup to render the expand/collapse control. Useful if you build a custom group cell renderer.
Hook for managing header row selection state. Used within custom header cell renderers to implement custom "select all" functionality.
Returns:
isIndeterminate: boolean- Whether some (but not all) rows are selectedisRowSelected: boolean- Whether all rows are selectedonRowSelectionChange: (event: SelectHeaderRowEvent) => void- Callback to change selection state
Example:
import { useLayoutEffect, useRef } from 'react';
function CustomHeaderCell() {
const { isIndeterminate, isRowSelected, onRowSelectionChange } = useHeaderRowSelection();
const checkboxRef = useRef<HTMLInputElement>(null);
useLayoutEffect(() => {
if (checkboxRef.current) {
checkboxRef.current.indeterminate = isIndeterminate && !isRowSelected;
}
}, [isIndeterminate, isRowSelected]);
return (
<input
ref={checkboxRef}
type="checkbox"
checked={isRowSelected}
onChange={(event) => onRowSelectionChange({ checked: event.target.checked })}
/>
);
}Hook for managing row selection state. Used within custom cell renderers to implement custom row selection.
Returns:
isRowSelectionDisabled: boolean- Whether selection is disabled for this rowisRowSelected: boolean- Whether this row is selectedonRowSelectionChange: (event: SelectRowEvent<R>) => void- Callback to change selection state
Example:
function CustomSelectCell({ row }: RenderCellProps<Row>) {
const { isRowSelectionDisabled, isRowSelected, onRowSelectionChange } = useRowSelection();
return (
<input
type="checkbox"
disabled={isRowSelectionDisabled}
checked={isRowSelected}
onChange={(event) =>
onRowSelectionChange({
row,
checked: event.currentTarget.checked,
isShiftClick: event.nativeEvent.shiftKey
})
}
/>
);
}The default header cell renderer. Renders sortable columns with sort indicators.
Props: RenderHeaderCellProps<TRow, TSummaryRow>
Example:
import { renderHeaderCell, type Column } from 'react-data-grid';
const columns: readonly Column<Row>[] = [
{
key: 'name',
name: 'Name',
sortable: true,
renderHeaderCell
}
];A basic text editor provided for convenience.
Props: RenderEditCellProps<TRow, TSummaryRow>
Example:
import { renderTextEditor, type Column } from 'react-data-grid';
const columns: readonly Column<Row>[] = [
{
key: 'title',
name: 'Title',
renderEditCell: renderTextEditor
}
];Renders the sort direction arrow icon.
Props: RenderSortIconProps
Renders the sort priority number for multi-column sorting.
Props: RenderSortPriorityProps
Renders a checkbox input with proper styling and accessibility.
Props: RenderCheckboxProps
Example:
import { DataGrid, renderCheckbox } from 'react-data-grid';
<DataGrid
renderers={{
renderCheckbox: (props) => renderCheckbox({ ...props, 'aria-label': 'Select row' })
}}
/>;The default group cell renderer for the columns used for grouping (groupBy prop). This renders the expand/collapse toggle.
Props: RenderGroupCellProps<TRow, TSummaryRow>
Example:
import { renderToggleGroup, type Column } from 'react-data-grid';
const columns: readonly Column<Row>[] = [
{
key: 'group',
name: 'Group',
renderGroupCell: renderToggleGroup
}
];The default cell renderer that renders the value of row[column.key].
Props: RenderCellProps<TRow, TSummaryRow>
Example:
import { renderValue, type Column } from 'react-data-grid';
const columns: readonly Column<Row>[] = [
{
key: 'title',
name: 'Title',
renderCell: renderValue
}
];Context for providing default renderers to DataGrids in your app.
Example:
import {
DataGridRenderersContext,
renderCheckbox,
renderSortIcon,
renderSortPriority,
type Renderers
} from 'react-data-grid';
// custom implementations of renderers
const defaultGridRenderers: Renderers<unknown, unknown> = {
renderCheckbox,
renderSortStatus(props) {
return (
<>
{renderSortIcon(props)}
{renderSortPriority(props)}
</>
);
}
};
function AppProvider({ children }) {
return (
<DataGridRenderersContext value={defaultGridRenderers}>{children}</DataGridRenderersContext>
);
}A pre-configured column for row selection. Includes checkbox renderers for the header row, regular rows, and group rows.
Example:
import { DataGrid, SelectColumn, type Column } from 'react-data-grid';
const columns: readonly Column<Row>[] = [SelectColumn, ...otherColumns];
function rowKeyGetter(row: Row) {
return row.id;
}
function MyGrid() {
return (
<DataGrid
columns={columns}
rows={rows}
rowKeyGetter={rowKeyGetter}
selectedRows={selectedRows}
onSelectedRowsChange={setSelectedRows}
/>
);
}The key used for the SelectColumn. Useful for identifying or filtering the select column.
Example:
import { SELECT_COLUMN_KEY } from 'react-data-grid';
const nonSelectColumns = columns.filter((column) => column.key !== SELECT_COLUMN_KEY);Defines the configuration for a column in the grid.
The name of the column. Displayed in the header cell by default.
A unique key to distinguish each column.
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.
const columns: Column<Row>[] = [
{
key: 'id',
name: 'ID',
width: 80, // Fixed width in pixels
resizable: false
},
{
key: 'name',
name: 'Name',
width: '30%', // Percentage width
minWidth: 100,
maxWidth: 400
},
{
key: 'description',
name: 'Description'
// No width specified - automatically sized
}
];Other examples:
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.
Default: 50 pixels
Minimum column width in pixels.
Maximum column width in pixels.
Class name(s) for cells. Can be a string or a function that returns a class name based on the row.
const columns: Column<Row>[] = [
{
key: 'status',
name: 'Status',
cellClass: (row) => `status-${row.status}`
},
{
key: 'price',
name: 'Price',
cellClass: 'text-right' // Static class
}
];.status-active {
color: green;
font-weight: bold;
}
.status-inactive {
color: gray;
}
.text-right {
text-align: right;
}Class name(s) for the header cell.
Class name(s) for summary cells. Can be a string or a function that returns a class name based on the summary row.
Render function to render the content of cells.
Render function to render the content of the header cell.
Render function to render the content of summary cells.
Render function to render the content of group cells when using TreeDataGrid.
Render function to render the content of edit cells. When set, the column is automatically set to be editable.
Control whether cells can be edited with renderEditCell.
Function to determine how many columns this cell should span. Returns the number of columns to span, or undefined for no spanning. See the ColSpanArgs type in the Types section below.
Example:
import type { Column } from 'react-data-grid';
const columns: readonly Column<Row>[] = [
{
key: 'title',
name: 'Title',
colSpan(args) {
if (args.type === 'ROW' && args.row.isFullWidth) {
return 5; // Span 5 columns for full-width rows
}
return undefined;
}
}
];Default: false
Determines whether the column is frozen, and on which edge. Frozen columns stay in place when the grid is scrolled horizontally.
'start'(ortruefor backward compatibility) — pins the column to the start edge (left in LTR, right in RTL).'end'— pins the column to the end edge (right in LTR, left in RTL).false(default) — the column scrolls with the rest of the grid.
const columns: readonly Column<Row>[] = [
{ key: 'id', name: 'ID', frozen: 'start' },
{ key: 'name', name: 'Name' },
{ key: 'actions', name: 'Actions', frozen: 'end' }
];Default: false
Enable resizing of the column.
Default: false
Enable sorting of the column.
Default: false
Enable dragging of the column.
Default: false
Sets the column sort order to be descending instead of ascending the first time the column is sorted.
Options for cell editing.
Default: false
Render the cell content in addition to the edit cell content. Enable this option when the editor is rendered outside the grid, like a modal for example.
Default: true
Commit changes when clicking outside the cell.
Default: true
Close the editor when the row value changes externally.
Defines a group of columns that share a common header.
interface ColumnGroup<R, SR = unknown> {
/** The name of the column group. It will be displayed in the header cell */
readonly name: string | ReactElement;
readonly headerCellClass?: Maybe<string>;
readonly children: readonly ColumnOrColumnGroup<R, SR>[];
}Example:
import type { ColumnOrColumnGroup } from 'react-data-grid';
const columns: readonly ColumnOrColumnGroup<Row>[] = [
{
name: 'Personal Info',
children: [
{ key: 'firstName', name: 'First Name' },
{ key: 'lastName', name: 'Last Name' }
]
}
];Union type representing either a Column or a ColumnGroup.
Extends Column with additional computed properties used internally by the grid. This is the type passed to render functions. The Column properties it redeclares, like width and renderCell, are resolved to their default values when not set.
interface CalculatedColumn<TRow, TSummaryRow = unknown> extends Column<TRow, TSummaryRow> {
/** The column group that contains the column, or `undefined` if the column is not in a group */
readonly parent: CalculatedColumnParent<TRow, TSummaryRow> | undefined;
/** The index of the column in the grid. Column groups are flattened and frozen columns are moved to their edge, so it can differ from the column's index in `columns` */
readonly idx: number;
/** Header row level, counted from the bottom header row: always `0` for columns, and negative for column groups */
readonly level: number;
readonly width: number | string;
readonly minWidth: number;
readonly maxWidth: number | undefined;
readonly resizable: boolean;
readonly sortable: boolean;
readonly draggable: boolean;
readonly frozen: ColumnFrozen;
readonly renderCell: (props: RenderCellProps<TRow, TSummaryRow>) => ReactNode;
readonly renderHeaderCell: (props: RenderHeaderCellProps<TRow, TSummaryRow>) => ReactNode;
}Represents a parent column group in the calculated column structure.
interface CalculatedColumnParent<R, SR> {
readonly name: string | ReactElement;
readonly parent: CalculatedColumnParent<R, SR> | undefined;
readonly idx: number;
readonly colSpan: number;
readonly level: number;
readonly headerCellClass?: Maybe<string>;
}Union type representing either a CalculatedColumnParent or a CalculatedColumn.
type CalculatedColumnOrColumnGroup<R, SR> = CalculatedColumnParent<R, SR> | CalculatedColumn<R, SR>;Arguments passed to TreeDataGrid's rowHeight prop when it is a function.
type RowHeightArgs<TRow> = { type: 'ROW'; row: TRow } | { type: 'GROUP'; row: GroupRow<TRow> };Example:
function getRowHeight(args: RowHeightArgs<Row>): number {
if (args.type === 'GROUP') {
return 40;
}
return args.row.isLarge ? 60 : 35;
}
<TreeDataGrid rowHeight={getRowHeight} ... />Props passed to custom cell renderers.
interface RenderCellProps<TRow, TSummaryRow = unknown> {
column: CalculatedColumn<TRow, TSummaryRow>;
row: TRow;
rowIdx: number;
isCellEditable: boolean;
tabIndex: number;
onRowChange: (row: TRow) => void;
}Example:
import type { RenderCellProps } from 'react-data-grid';
function renderCell({ row, column, onRowChange }: RenderCellProps<MyRow>) {
return (
<div>
{row[column.key]}
<button onClick={() => onRowChange({ ...row, updated: true })}>Update</button>
</div>
);
}Props passed to custom header cell renderers.
interface RenderHeaderCellProps<TRow, TSummaryRow = unknown> {
column: CalculatedColumn<TRow, TSummaryRow>;
sortDirection: SortDirection | undefined;
priority: number | undefined;
tabIndex: number;
}Props passed to custom edit cell renderers (editors).
interface RenderEditCellProps<TRow, TSummaryRow = unknown> {
column: CalculatedColumn<TRow, TSummaryRow>;
row: TRow;
rowIdx: number;
onRowChange: (row: TRow, commitChanges?: boolean) => void;
onClose: (commitChanges?: boolean, shouldFocus?: boolean) => void;
}Example:
import type { RenderEditCellProps } from 'react-data-grid';
function CustomEditor({ row, column, onRowChange, onClose }: RenderEditCellProps<MyRow>) {
return (
<input
autoFocus
value={row[column.key]}
onChange={(event) => onRowChange({ ...row, [column.key]: event.target.value })}
onBlur={() => onClose(true)}
/>
);
}Props passed to summary cell renderers.
interface RenderSummaryCellProps<TSummaryRow, TRow = unknown> {
column: CalculatedColumn<TRow, TSummaryRow>;
row: TSummaryRow;
tabIndex: number;
}Props passed to group cell renderers when using TreeDataGrid.
interface RenderGroupCellProps<TRow, TSummaryRow = unknown> {
groupKey: unknown;
column: CalculatedColumn<TRow, TSummaryRow>;
row: GroupRow<TRow>;
childRows: readonly TRow[];
isExpanded: boolean;
tabIndex: number;
toggleGroup: () => void;
}Props passed to custom row renderers.
interface RenderRowProps<TRow, TSummaryRow = unknown> {
row: TRow;
iterateOverViewportColumnsForRow: IterateOverViewportColumnsForRow<TRow, TSummaryRow>;
rowIdx: number;
activeCellIdx: number | undefined;
isRowSelectionDisabled: boolean;
isRowSelected: boolean;
gridRowStart: number;
draggedOverCellIdx: number | undefined;
activeCellEditor: ReactElement<RenderEditCellProps<TRow>> | undefined;
onRowChange: (column: CalculatedColumn<TRow, TSummaryRow>, rowIdx: number, newRow: TRow) => void;
rowClass: Maybe<(row: TRow, rowIdx: number) => Maybe<string>>;
isTreeGrid: boolean;
// ... and event handlers
}Props passed to the cell renderer when using renderers.renderCell.
Shares a base type with row render props (DOM props and cell event handlers) but only includes cell-specific fields like column, row, rowIdx, colSpan, and position state.
Custom renderer configuration for the grid.
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>;
}Arguments passed to cell mouse event handlers.
interface CellMouseArgs<TRow, TSummaryRow = unknown> {
/** The column object of the cell. */
column: CalculatedColumn<TRow, TSummaryRow>;
/** The row object of the cell. */
row: TRow;
/** The row index of the cell. */
rowIdx: number;
/** Function to manually focus the cell. Pass `true` to immediately start editing. */
setActivePosition: (enableEditor?: boolean) => void;
}Example:
import type { CellMouseArgs, CellMouseEvent } from 'react-data-grid';
function onCellClick(args: CellMouseArgs<Row>, event: CellMouseEvent) {
console.log('Clicked cell at row', args.rowIdx, 'column', args.column.key);
args.setActivePosition(true); // Focus and start editing
}Handler type used by onCellMouseDown, onCellClick, onCellDoubleClick, and onCellContextMenu. This helper type is not exported; the shape is shown for reference.
type CellMouseEventHandler<TRow, TSummaryRow> = Maybe<
(args: CellMouseArgs<TRow, TSummaryRow>, event: CellMouseEvent) => void
>;Adds grid-specific methods to a React event. This helper type is not exported; the shape is shown for reference.
type CellEvent<E extends React.SyntheticEvent<HTMLDivElement>> = E & {
/** Prevents the default grid behavior for this event */
preventGridDefault: () => void;
/** Returns whether `preventGridDefault` was called */
isGridDefaultPrevented: () => boolean;
};Extends React.MouseEvent<HTMLDivElement> with grid-specific methods.
type CellMouseEvent = CellEvent<React.MouseEvent<HTMLDivElement>>;Example:
import type { CellMouseArgs, CellMouseEvent } from 'react-data-grid';
function onCellMouseDown(args: CellMouseArgs<Row>, event: CellMouseEvent) {
if (args.column.key === 'actions') {
event.preventGridDefault(); // Prevent cell focus
}
}Extends React.KeyboardEvent<HTMLDivElement> with grid-specific methods.
type CellKeyboardEvent = CellEvent<React.KeyboardEvent<HTMLDivElement>>;Type alias for React.ClipboardEvent<HTMLDivElement>. Used for copy and paste events.
type CellClipboardEvent = React.ClipboardEvent<HTMLDivElement>;Arguments passed to the onCellKeyDown handler. The shape differs based on whether the cell is in ACTIVE or EDIT mode.
ACTIVE mode:
interface ActiveCellKeyDownArgs<TRow, TSummaryRow = unknown> {
mode: 'ACTIVE';
column: CalculatedColumn<TRow, TSummaryRow> | undefined;
row: TRow | undefined;
rowIdx: number;
setActivePosition: (position: Position, options?: SetActivePositionOptions) => void;
}EDIT mode:
interface EditCellKeyDownArgs<TRow, TSummaryRow = unknown> {
mode: 'EDIT';
column: CalculatedColumn<TRow, TSummaryRow>;
row: TRow;
rowIdx: number;
navigate: () => void;
onClose: (commitChanges?: boolean, shouldFocus?: boolean) => void;
}Example:
import type { CellKeyboardEvent, CellKeyDownArgs } from 'react-data-grid';
function onCellKeyDown(args: CellKeyDownArgs<Row>, event: CellKeyboardEvent) {
if (args.mode === 'EDIT' && event.key === 'Escape') {
args.onClose(false); // Close without committing
event.preventGridDefault();
}
}Arguments passed to onActivePositionChange.
interface PositionChangeArgs<TRow, TSummaryRow = unknown> {
/** row index of the active position */
rowIdx: number;
/**
* row object of the active position,
* undefined if the active position is on a header or summary row
*/
row: TRow | undefined;
/**
* column object of the active position,
* undefined if the active position is a row instead of a cell
*/
column: CalculatedColumn<TRow, TSummaryRow> | undefined;
}Arguments passed to onCellCopy.
interface CellCopyArgs<TRow, TSummaryRow = unknown> {
column: CalculatedColumn<TRow, TSummaryRow>;
row: TRow;
}Arguments passed to onCellPaste.
interface CellPasteArgs<TRow, TSummaryRow = unknown> {
column: CalculatedColumn<TRow, TSummaryRow>;
row: TRow;
}Arguments passed to the colSpan function.
type ColSpanArgs<TRow, TSummaryRow> =
| { readonly type: 'HEADER' }
| { readonly type: 'ROW'; readonly row: TRow }
| { readonly type: 'SUMMARY'; readonly row: TSummaryRow };Example:
import type { Column } from 'react-data-grid';
const columns: readonly Column<Row>[] = [
{
key: 'title',
name: 'Title',
colSpan(args) {
if (args.type === 'ROW' && args.row.isFullWidth) {
return 3; // Span 3 columns
}
return undefined;
}
}
];Describes a sorted column.
interface SortColumn {
readonly columnKey: string;
readonly direction: SortDirection;
}type SortDirection = 'ASC' | 'DESC';Data provided to the onRowsChange callback.
interface RowsChangeData<R, SR = unknown> {
/** Indexes of the rows that changed */
indexes: number[];
/** The column of the cells that changed */
column: CalculatedColumn<R, SR>;
}Event object for row selection changes.
interface SelectRowEvent<TRow> {
row: TRow;
checked: boolean;
isShiftClick: boolean;
}Event object for header row selection changes.
interface SelectHeaderRowEvent {
checked: boolean;
}Event object for drag-fill operations.
interface FillEvent<TRow> {
columnKey: string;
sourceRow: TRow;
targetRow: TRow;
}Used with the onFill prop to handle cell value dragging.
Represents a group row in TreeDataGrid. This helper type is not exported; the shape is shown for reference.
interface GroupRow<TRow> {
readonly childRows: readonly TRow[];
readonly id: string;
readonly parentId: unknown;
readonly groupKey: unknown;
readonly isExpanded: boolean;
readonly level: number;
readonly posInSet: number;
readonly setSize: number;
readonly startRowIndex: number;
}A map of column widths.
type ColumnWidths = ReadonlyMap<string, ColumnWidth>;
interface ColumnWidth {
readonly type: 'resized' | 'measured';
readonly width: number;
}Used with the columnWidths and onColumnWidthsChange props to control column widths externally.
Represents a cell position in the grid.
interface Position {
/** Column index, or `-1` for the whole row in `TreeDataGrid` */
readonly idx: number;
/** Row index: `0` is the first row, header rows and top summary rows have negative indexes, and bottom summary rows come after the last row */
readonly rowIdx: number;
}Options for programmatically updating the grid's active position.
interface SetActivePositionOptions {
enableEditor?: Maybe<boolean>;
shouldFocus?: Maybe<boolean>;
}Props for custom checkbox renderers.
interface RenderCheckboxProps {
/** Whether the checkbox is checked */
checked?: boolean | undefined;
/** Whether the checkbox is in an indeterminate state, like the select all checkbox when only some rows are selected */
indeterminate?: boolean | undefined;
/** Whether the checkbox is disabled */
disabled?: boolean | undefined;
/** Called when the checkbox is toggled. `shift` is `true` if the Shift key was held, to select a range of rows */
onChange: (checked: boolean, shift: boolean) => void;
/** `0` when the checkbox's cell is active and `-1` otherwise, so the grid can move focus from the cell to the checkbox */
tabIndex?: number | undefined;
/** Accessible label for the checkbox */
'aria-label'?: string | undefined;
/** ID of the element that labels the checkbox */
'aria-labelledby'?: string | undefined;
}Props for custom sort status renderers.
interface RenderSortStatusProps extends RenderSortIconProps, RenderSortPriorityProps {}Props for custom sort icon renderers.
interface RenderSortIconProps {
/** The sort direction of the column, or `undefined` if it is not sorted */
sortDirection: SortDirection | undefined;
}Props for custom sort priority renderers.
interface RenderSortPriorityProps {
/** The column's position in `sortColumns`, starting at 1, when sorting by multiple columns, or `undefined` otherwise */
priority: number | undefined;
}Handle type assigned to a grid's ref for programmatic grid control.
interface DataGridHandle {
element: HTMLDivElement | null;
scrollToCell: (position: PartialPosition) => void;
setActivePosition: (position: Position, options?: SetActivePositionOptions) => void;
}Example:
import { useRef } from 'react';
import { DataGrid, type DataGridHandle } from 'react-data-grid';
function MyGrid() {
const gridRef = useRef<DataGridHandle>(null);
function scrollToTop() {
gridRef.current?.scrollToCell({ rowIdx: 0 });
}
return <DataGrid ref={gridRef} columns={columns} rows={rows} />;
}Default options applied to all columns.
type DefaultColumnOptions<TRow, TSummaryRow> = Pick<
Column<TRow, TSummaryRow>,
| 'renderCell'
| 'renderHeaderCell'
| 'width'
| 'minWidth'
| 'maxWidth'
| 'resizable'
| 'sortable'
| 'draggable'
>;Text direction of the grid.
type Direction = 'ltr' | 'rtl';Utility type for optional values. This helper type is not exported; the shape is shown for reference.
type Maybe<T> = T | undefined | null;R,TRow: Row typeSR,TSummaryRow: Summary row typeK: Row key type