Excel-like grid component built with React, with editors, keyboard navigation, copy & paste, and the like
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Core building blocks and advanced customization options for creating custom grid rendering and specialized functionality.
Low-level components that make up the grid structure, available for advanced customization scenarios.
/** Core grid row component */
const Row: React.ComponentType<RowProps>;
/** Individual cell component */
const Cell: React.ComponentType<CellProps>;
/** Header cell component */
const HeaderCell: React.ComponentType<HeaderCellProps>;
/** Empty child row placeholder */
const EmptyChildRow: React.ComponentType<EmptyChildRowProps>;Individual row component that renders a collection of cells.
interface RowProps {
/** Row index in the dataset */
idx: number;
/** Starting column index for visible columns */
visibleStart: number;
/** Ending column index for visible columns */
visibleEnd: number;
/** Row data object */
row: any;
/** Row height in pixels */
height: number;
/** Array of column definitions */
columns: Column[];
/** Whether the row is selected */
isSelected?: boolean;
/** Whether the row is being scrolled */
isScrolling?: boolean;
/** CSS classes to apply to the row */
extraClasses?: string;
/** Function to get cell actions */
getCellActions?: (column: Column, row: any) => CellAction[];
/** Cell selection range */
cellSelection?: SelectionRange;
/** Event handlers for row interactions */
onRowClick?: (rowIdx: number, row: any) => void;
onRowDoubleClick?: (rowIdx: number, row: any) => void;
onCellClick?: (position: Position) => void;
onCellDoubleClick?: (position: Position) => void;
onCellContextMenu?: (position: Position) => void;
onDragEnter?: (overRowIdx: number) => void;
}
/**
* Row component for rendering individual grid rows
* Handles cell rendering, selection, and user interactions
*/
const Row: React.ComponentType<RowProps>;Individual cell component that handles content rendering, editing, and interactions.
interface CellProps {
/** Column definition */
column: Column;
/** Cell value */
value: any;
/** Complete row data */
row: any;
/** Row index */
rowIdx: number;
/** Column index */
idx: number;
/** Whether the cell is selected */
isSelected?: boolean;
/** Whether the cell is being edited */
isEditing?: boolean;
/** Whether the cell can be copied */
isCopied?: boolean;
/** Cell height in pixels */
height: number;
/** Event handlers */
onCellClick?: (position: Position) => void;
onCellDoubleClick?: (position: Position) => void;
onCellContextMenu?: (position: Position) => void;
onCellExpand?: (args: CellExpandArgs) => void;
onCommit?: (args: CommitArgs) => void;
onCommitCancel?: () => void;
}
/**
* Cell component for rendering individual grid cells
* Handles content display, editing, and cell-specific interactions
*/
const Cell: React.ComponentType<CellProps>;Header cell component for column headers with sorting, filtering, and resizing capabilities.
interface HeaderCellProps {
/** Column definition */
column: Column;
/** Row type (header, filter, etc.) */
rowType: string;
/** Cell height in pixels */
height: number;
/** Event handlers */
onSort?: (columnKey: string, direction: SortDirection) => void;
onResize?: (column: Column, width: number) => void;
onHeaderDrop?: () => void;
onFilterChange?: (filter: Filter) => void;
/** Current sort configuration */
sortColumn?: string;
sortDirection?: SortDirection;
/** Whether the column can be dragged */
draggableHeaderCell?: React.ComponentType<any>;
}
/**
* Header cell component for column headers
* Supports sorting, filtering, resizing, and custom header rendering
*/
const HeaderCell: React.ComponentType<HeaderCellProps>;Placeholder component for empty child rows in hierarchical data structures.
interface EmptyChildRowProps {
/** Row height in pixels */
height: number;
/** Column definitions */
columns: Column[];
/** Additional CSS classes */
extraClasses?: string;
}
/**
* Empty child row component for hierarchical data
* Renders placeholder rows for expanded parent rows with no children
*/
const EmptyChildRow: React.ComponentType<EmptyChildRowProps>;Using core components for advanced customization scenarios.
Custom Row Renderer Example:
import React from 'react';
import ReactDataGrid, { Row } from 'react-data-grid';
const CustomRow = (props) => {
const { row, idx } = props;
// Add custom styling based on row data
const customStyle = {
backgroundColor: row.priority === 'high' ? '#fff3cd' : undefined,
fontWeight: row.isImportant ? 'bold' : 'normal'
};
return (
<div style={customStyle}>
<Row {...props} />
</div>
);
};
// Usage with custom row renderer
const GridWithCustomRows = () => {
return (
<ReactDataGrid
columns={columns}
rowGetter={i => rows[i]}
rowsCount={rows.length}
minHeight={400}
rowGroupRenderer={CustomRow}
/>
);
};Custom Cell Actions Example:
import ReactDataGrid from 'react-data-grid';
const getCellActions = (column, row) => {
if (column.key === 'actions') {
return [
{
icon: 'edit',
text: 'Edit',
callback: () => console.log('Edit:', row)
},
{
icon: 'delete',
text: 'Delete',
callback: () => console.log('Delete:', row)
}
];
}
return [];
};
const GridWithActions = () => {
return (
<ReactDataGrid
columns={columns}
rowGetter={i => rows[i]}
rowsCount={rows.length}
minHeight={400}
getCellActions={getCellActions}
/>
);
};Supporting types and interfaces for component customization.
interface Position {
/** Column index */
idx: number;
/** Row index */
rowIdx: number;
}
interface SelectionRange {
/** Top-left position */
topLeft: Position;
/** Bottom-right position */
bottomRight: Position;
}
interface CellAction {
/** Action icon identifier */
icon: string;
/** Display text for the action */
text: string;
/** Callback function when action is triggered */
callback: () => void;
/** Whether the action is disabled */
disabled?: boolean;
}
interface CellExpandArgs {
/** Row index */
rowIdx: number;
/** Column index */
idx: number;
/** Row data */
rowData: any;
/** Expand arguments */
expandArgs: any;
}
interface CommitArgs {
/** Cell position */
position: Position;
/** Updated value */
updated: any;
/** Update action type */
action: string;
}Complex customization scenarios using core components and custom renderers.
Custom Header with Actions:
import React from 'react';
const CustomHeaderCell = ({ column, onSort, sortColumn, sortDirection }) => {
const handleSort = () => {
const newDirection =
sortColumn === column.key && sortDirection === 'ASC' ? 'DESC' : 'ASC';
onSort(column.key, newDirection);
};
const getSortIcon = () => {
if (sortColumn !== column.key) return '↕️';
return sortDirection === 'ASC' ? '↑' : '↓';
};
return (
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '8px'
}}>
<span>{column.name}</span>
<div style={{ display: 'flex', gap: '4px' }}>
{column.sortable && (
<button
onClick={handleSort}
style={{ border: 'none', background: 'none', cursor: 'pointer' }}
>
{getSortIcon()}
</button>
)}
<button
onClick={() => console.log('Filter', column.key)}
style={{ border: 'none', background: 'none', cursor: 'pointer' }}
>
🔍
</button>
</div>
</div>
);
};
// Usage in column definitions
const customColumns = [
{
key: 'id',
name: 'ID',
headerRenderer: <CustomHeaderCell />
},
{
key: 'name',
name: 'Name',
sortable: true,
headerRenderer: <CustomHeaderCell />
}
];Custom Cell with Rich Content:
const RichCell = ({ value, row, column }) => {
if (column.key === 'profile') {
return (
<div style={{
display: 'flex',
alignItems: 'center',
gap: '8px',
padding: '4px'
}}>
<img
src={row.avatar}
alt={row.name}
style={{
width: '32px',
height: '32px',
borderRadius: '50%'
}}
/>
<div>
<div style={{ fontWeight: 'bold' }}>{row.name}</div>
<div style={{ fontSize: '12px', color: '#666' }}>{row.email}</div>
</div>
</div>
);
}
return <div>{value}</div>;
};
const richColumns = [
{ key: 'id', name: 'ID' },
{
key: 'profile',
name: 'Profile',
formatter: <RichCell />
},
{ key: 'status', name: 'Status' }
];Custom Toolbar Integration:
const CustomToolbar = ({ onAddRow, onDeleteRows, selectedCount }) => {
return (
<div style={{
padding: '8px',
backgroundColor: '#f8f9fa',
borderBottom: '1px solid #dee2e6',
display: 'flex',
gap: '8px',
alignItems: 'center'
}}>
<button onClick={onAddRow}>Add Row</button>
<button
onClick={onDeleteRows}
disabled={selectedCount === 0}
>
Delete Selected ({selectedCount})
</button>
<div style={{ marginLeft: 'auto' }}>
Total: {rows.length} rows
</div>
</div>
);
};
const GridWithToolbar = () => {
const [selectedRows, setSelectedRows] = useState([]);
return (
<ReactDataGrid
columns={columns}
rowGetter={i => rows[i]}
rowsCount={rows.length}
minHeight={400}
toolbar={
<CustomToolbar
onAddRow={handleAddRow}
onDeleteRows={handleDeleteRows}
selectedCount={selectedRows.length}
/>
}
rowSelection={{
showCheckbox: true,
onRowsSelected: setSelectedRows,
selectBy: { indexes: selectedRows }
}}
/>
);
};Tips for optimizing custom components for better grid performance.
import React, { memo, useMemo } from 'react';
// Memoized custom formatter
const OptimizedFormatter = memo(({ value, row, column }) => {
const processedValue = useMemo(() => {
return expensiveProcessing(value);
}, [value]);
return <div>{processedValue}</div>;
});
// Memoized row component
const OptimizedRow = memo((props) => {
return <Row {...props} />;
}, (prevProps, nextProps) => {
// Custom comparison logic
return (
prevProps.row === nextProps.row &&
prevProps.isSelected === nextProps.isSelected &&
prevProps.visibleStart === nextProps.visibleStart &&
prevProps.visibleEnd === nextProps.visibleEnd
);
});Install with Tessl CLI
npx tessl i tessl/npm-react-data-grid@6.1.1