Data GridNew
A full-featured data grid with sorting, selection, column resizing, pinned columns, drag-and-drop row reorder, expandable rows, virtualization, and async loading — built on the Table component.
Import
import { DataGrid } from '@darkcode-ui/react';Usage
The DataGrid component takes a flat data array, a columns definition, and a getRowId function. It renders a fully accessible table with built-in support for sorting, selection, column resizing, and more.
"use client";
import type {DataGridColumn} from "@darkcode-ui/react";
import {Button, Chip, DataGrid, Dropdown, Label} from "@darkcode-ui/react";Anatomy
Unlike most DarkCode UI components, DataGrid is not a compound component — it has no DataGrid.* sub-parts. Everything is configured through props and column definitions.
import { DataGrid } from '@darkcode-ui/react';
<DataGrid
aria-label="Payments"
columns={columns}
data={payments}
getRowId={(item) => item.id}
/>Column Definitions
Columns are defined as an array of DataGridColumn<T> objects. Each column has an id, a header, and either an accessorKey (to read a value from the row object) or a custom cell renderer.
import type { DataGridColumn } from '@darkcode-ui/react';
interface Payment {
id: string;
customer: string;
amount: number;
status: 'succeeded' | 'failed';
}
const columns: DataGridColumn<Payment>[] = [
{
id: 'customer',
header: 'Customer',
accessorKey: 'customer',
isRowHeader: true,
allowsSorting: true,
},
{
id: 'amount',
header: 'Amount',
accessorKey: 'amount',
align: 'end',
cell: (item) => `$${item.amount.toFixed(2)}`,
},
{
id: 'status',
header: 'Status',
accessorKey: 'status',
},
];The cell function receives the full row item and the column definition and can return any ReactNode. The header property accepts a string, a ReactNode, or a render function that receives { sortDirection } for sortable columns.
Sorting
Mark columns as sortable with allowsSorting: true. In uncontrolled mode, the DataGrid sorts data client-side using locale-aware comparison (or a custom sortFn on the column). For server-side sorting, pass a controlled sortDescriptor and handle onSortChange.
// Uncontrolled (client-side)
<DataGrid
defaultSortDescriptor={{ column: 'customer', direction: 'ascending' }}
// ...
/>
// Controlled (server-side)
<DataGrid
sortDescriptor={sortDescriptor}
onSortChange={setSortDescriptor}
// ...
/>Row Selection
Enable row selection with selectionMode and showSelectionCheckboxes. Both "single" and "multiple" modes are supported, with controlled (selectedKeys + onSelectionChange) or uncontrolled (defaultSelectedKeys) state.
Pinned Columns
Pin columns to the start or end edge with pinned: "start" or pinned: "end" so they stay visible during horizontal scroll. Pinned columns must have a numeric width or minWidth. A separator appears on the boundary column whenever the pinned group detaches from the scrollable content.
"use client";
import type {DataGridColumn} from "@darkcode-ui/react";
import {Chip, DataGrid, Link} from "@darkcode-ui/react";Drag and Drop
Enable row reorder with the onReorder callback. The DataGrid renders built-in drag handles with keyboard support (Enter to grab, arrows to move, Enter to drop) and fires the callback with the reordered data array. For advanced scenarios, pass dragAndDropHooks from React Aria's useDragAndDrop instead — it takes precedence over onReorder.
With expandable rows enabled, the built-in reorder applies to top-level rows only.
"use client";
import type {DataGridColumn} from "@darkcode-ui/react";
import {Chip, DataGrid} from "@darkcode-ui/react";Expandable Rows
Render hierarchical data by providing a getChildren function. The DataGrid recursively renders child rows, auto-generates a chevron toggle in the treeColumn, and indents each nested level by treeIndent pixels (default 20, set 0 to disable).
The treeColumn prop specifies which column displays the chevron. If omitted, it defaults to the first isRowHeader column (or the first column). Use defaultExpandedKeys for uncontrolled expansion, or pair expandedKeys with onExpandedChange for controlled behavior. With client-side sorting, each sibling group is sorted independently.
"use client";
import type {DataGridColumn} from "@darkcode-ui/react";
import {Chip, DataGrid} from "@darkcode-ui/react";Editable Cells
Use the cell render function to embed any interactive component — selects, number steppers, switches, text fields, and more.
"use client";
import type {DataGridColumn} from "@darkcode-ui/react";
import {Chip, DataGrid, ListBox, NumberField, Select, Switch} from "@darkcode-ui/react";Empty State
Provide a renderEmptyState function to display a custom empty state when data is empty.
"use client";
import type {DataGridColumn} from "@darkcode-ui/react";
import {Button, DataGrid, EmptyState} from "@darkcode-ui/react";Async Loading
Use onLoadMore, isLoadingMore, and loadMoreContent to implement infinite scroll loading. The DataGrid renders a sentinel row that triggers onLoadMore when it scrolls into view.
"use client";
import type {DataGridColumn} from "@darkcode-ui/react";
import {Chip, DataGrid, Spinner} from "@darkcode-ui/react";Column Resizing
Enable column resizing with allowsColumnResize on the DataGrid and allowsResizing on individual columns. Columns should have minWidth set. Track widths with onColumnResize and onColumnResizeEnd.
Virtualization
Enable row virtualization for large datasets (1,000+ rows) with the virtualized prop. Only visible rows are rendered to the DOM. Set rowHeight and headingHeight to match your row content, and constrain the height via contentClassName.
"use client";
import type {DataGridColumn, DataGridSelection} from "@darkcode-ui/react";
import {Chip, DataGrid, SearchField, ToggleButton, ToggleButtonGroup} from "@darkcode-ui/react";Bulk Actions
Combine row selection with an ActionBar to provide bulk operations like export, archive, or delete.
"use client";
import type {DataGridColumn, DataGridSelection} from "@darkcode-ui/react";
import {ActionBar, Avatar, Chip, DataGrid} from "@darkcode-ui/react";Users
A minimal user directory using the "secondary" variant with accessorKey-only columns and a row action link — no selection, no sorting.
"use client";
import type {DataGridColumn} from "@darkcode-ui/react";
import {Button, DataGrid, Link} from "@darkcode-ui/react";Team Members
A full-featured HR table with controlled sorting, multi-selection, pinned columns, column resizing, column visibility toggling, and search.
"use client";
import type {DataGridColumn, DataGridSelection, DataGridSortDescriptor} from "@darkcode-ui/react";
import {Avatar, Button, Chip, DataGrid, Dropdown, Label, SearchField} from "@darkcode-ui/react";Servers
A server monitoring dashboard with controlled sorting, selection, search, and rich cell renderers including sparkline charts and circular progress indicators.
"use client";
import type {DataGridColumn, DataGridSelection, DataGridSortDescriptor} from "@darkcode-ui/react";
import {Button, Chip, DataGrid, ProgressCircle, SearchField} from "@darkcode-ui/react";Styling
CSS Classes
.data-grid— Root wrapper. Defines the--data-grid-*custom properties..data-grid__selection-column/.data-grid__selection-cell— Narrow column for the select-all and row checkboxes. Width from--data-grid-selection-column-width..data-grid__drag-handle-column/.data-grid__drag-handle-cell— Narrow column for the drag handles. Width from--data-grid-drag-handle-column-width..data-grid__drag-handle— The grip button inside each row.cursor: grab,grabbingwhile pressed..data-grid__sort-icon— Chevron next to sortable column headers. Rotates 180° when descending..data-grid__empty-state— Centered container for the empty state message..data-grid__tree-cell— Flex wrapper inside thetreeColumncell holding the chevron toggle and cell content. Per-level indentation via--data-grid-tree-indent..data-grid__tree-toggle/.data-grid__tree-toggle-icon— Expand/collapse chevron button and icon. The icon rotates 90° when expanded via[data-expanded]..data-grid__tree-toggle-spacer— Invisible placeholder for leaf rows so content aligns with siblings that have a chevron.
Data Attributes
[data-align="center" | "end"]— Set on header and body cells from the columnalignoption.[data-vertical-align="top" | "middle" | "bottom"]— Set on the root from theverticalAlignprop.[data-pinned="start" | "end"]— Sticky pinned cells.[data-pinned-edge]— Boundary column of a pinned group; shows the separator.[data-pinned-start-detached]/[data-pinned-end-detached]— Set on the root while the content is scrolled away from the pinned edge.
CSS Variables
--data-grid-selection-column-width— Width of the selection checkbox column (default40px).--data-grid-drag-handle-column-width— Width of the drag handle column (default32px).--data-grid-tree-toggle-size— Size of the expand/collapse chevron button and its leaf-row spacer (default24px).--data-grid-tree-gap— Gap between the chevron/spacer and the cell content (default4px).--data-grid-tree-indent— Indentation per nested level (default20px, set from thetreeIndentprop).
API Reference
DataGrid
Accepts a generic type parameter T for the row data shape.
| Prop | Type | Default | Description |
|---|---|---|---|
data | T[] | - | Row data array |
columns | DataGridColumn<T>[] | - | Column definitions |
getRowId | (item: T) => Key | - | Extracts a unique key from each row item |
aria-label | string | - | Accessible label for the table. Required |
variant | 'primary' | 'secondary' | 'primary' | Visual variant passed to the underlying Table |
className | string | - | Additional className for the root wrapper |
contentClassName | string | - | Additional className for the inner <table> element |
scrollContainerClassName | string | - | Additional className for the scroll container |
verticalAlign | 'top' | 'middle' | 'bottom' | 'middle' | Vertical alignment of cell content |
selectionMode | 'none' | 'single' | 'multiple' | 'none' | Row selection mode |
selectedKeys | DataGridSelection | - | Controlled selected row keys |
defaultSelectedKeys | DataGridSelection | - | Default selected row keys (uncontrolled) |
onSelectionChange | (keys: DataGridSelection) => void | - | Callback when selection changes |
selectionBehavior | 'toggle' | 'replace' | 'toggle' | Selection interaction model |
showSelectionCheckboxes | boolean | false | Auto-prepend a checkbox column for selection |
disabledKeys | Iterable<Key> | - | Keys of rows that should be disabled |
sortDescriptor | DataGridSortDescriptor | - | Controlled sort descriptor |
defaultSortDescriptor | DataGridSortDescriptor | - | Default sort descriptor (uncontrolled client-side sorting) |
onSortChange | (descriptor: DataGridSortDescriptor) => void | - | Callback when sort changes. Fires in both modes |
allowsColumnResize | boolean | false | Enable column resizing on columns that opt in |
onColumnResize | (widths: Map<Key, DataGridColumnSize>) => void | - | Callback during column resize |
onColumnResizeEnd | (widths: Map<Key, DataGridColumnSize>) => void | - | Callback when resize ends |
onReorder | (event: DataGridReorderEvent<T>) => void | - | Enables built-in drag-and-drop row reorder |
dragAndDropHooks | DataGridDragAndDropHooks | - | Advanced React Aria drag-and-drop hooks. Overrides onReorder |
onRowAction | (key: Key) => void | - | Callback when a row is actioned |
renderEmptyState | () => ReactNode | - | Render function for the empty state |
onLoadMore | () => void | - | Callback when the load-more sentinel scrolls into view |
isLoadingMore | boolean | false | Whether more data is currently being fetched |
loadMoreContent | ReactNode | - | Content inside the load-more sentinel row |
virtualized | boolean | false | Enable row virtualization for large datasets |
rowHeight | number | 42 | Fixed row height in pixels (virtualized mode) |
headingHeight | number | 36 | Header row height in pixels (virtualized mode) |
getChildren | (item: T) => T[] | undefined | - | Return child rows. Enables expandable/tree rows |
treeColumn | string | - | Column id that displays the expand/collapse chevron |
expandedKeys | DataGridSelection | - | Controlled set of expanded row keys |
defaultExpandedKeys | DataGridSelection | - | Default expanded row keys (uncontrolled) |
onExpandedChange | (keys: DataGridSelection) => void | - | Callback when expanded rows change |
treeIndent | number | 20 | Pixels of indentation per nested level. 0 disables |
DataGridColumn
Column definition object passed to the columns prop.
| Property | Type | Default | Description |
|---|---|---|---|
id | string | - | Unique column identifier. Used as the sort key. Required |
header | ReactNode | (info) => ReactNode | - | Header content. Render function receives { sortDirection } |
accessorKey | keyof T & string | - | Key on T to read the cell value from |
cell | (item: T, column) => ReactNode | - | Custom cell renderer |
isRowHeader | boolean | false | Mark this column as the row header (for accessibility) |
allowsSorting | boolean | false | Allow this column to be sorted |
sortFn | (a: T, b: T) => number | - | Custom sort comparator |
allowsResizing | boolean | - | Allow resizing (with allowsColumnResize on the grid) |
width | DataGridColumnSize | - | Initial column width (px, %, or fr) |
minWidth | number | - | Minimum column width |
maxWidth | number | - | Maximum column width |
align | 'start' | 'center' | 'end' | 'start' | Cell text alignment |
headerClassName | string | - | Additional className for every <th> of this column |
cellClassName | string | - | Additional className for every <td> of this column |
pinned | 'start' | 'end' | - | Pin this column. Requires numeric width or minWidth |
DataGridReorderEvent
Event object passed to the onReorder callback.
| Property | Type | Description |
|---|---|---|
keys | Set<Key> | The keys that were moved |
target | { key: Key; dropPosition: 'before' | 'after' } | The target row key and drop position |
reorderedData | T[] | The full reordered data array after applying the move |
Type Exports
| Type | Description |
|---|---|
DataGridSelection | A set of selected keys, or 'all' |
DataGridSortDescriptor | The current sort column and direction |
DataGridSortDirection | 'ascending' | 'descending' |
DataGridColumnSize | A number, numeric string, percentage, or fractional unit |
DataGridDragAndDropHooks | Hooks returned by React Aria's useDragAndDrop |





