react-data-grid-table-excel
v0.1.1
Published
A spreadsheet-like React data grid with editing, sorting, and Ant Design styling
Downloads
184
Maintainers
Readme
react-data-grid-table-excel
A spreadsheet-like React data grid component with cell editing, column sorting, frozen columns, and autocomplete — styled with Ant Design tokens.
Features
- Spreadsheet UI — Excel/Google Sheets look and feel with borders, hover highlights, and cell selection outlines
- Inline cell editing — Click any editable cell to type a new value. Commit with Enter or blur, cancel with Escape
- Autocomplete suggestions — Provide a list of options per column for filtered, typeahead suggestions while editing
- Column sorting — Click headers to cycle through ascending, descending, and unsorted states. Supports controlled and uncontrolled modes
- Frozen columns — Pin columns to the left so they stay visible during horizontal scroll. Users can toggle freeze via checkboxes in the header
- Dynamic cell styling — Apply conditional background/text colors per cell based on value (e.g., status color coding)
- Custom header styles — Set background color, font weight, and alignment per column header
- Custom cell renderers — Override cell display with a custom React render function
- TypeScript — Full generic type inference on
DataGrid<TRow>with exported types forColumnDef,DataGridProps,SortState, and more - Ant Design theming — Uses
antd-styledesign tokens, so colors, fonts, and spacing follow your Ant Design theme automatically
Installation
npm install react-data-grid-table-excelPeer dependencies
Make sure these are installed in your project:
npm install react react-dom antd antd-style| Peer dependency | Version |
|-----------------|----------|
| react | >= 18 |
| react-dom | >= 18 |
| antd | >= 5 |
| antd-style | >= 3 |
Quick Start
import { DataGrid } from 'react-data-grid-table-excel';
import type { ColumnDef } from 'react-data-grid-table-excel';
interface Employee {
id: number;
name: string;
role: string;
salary: number;
}
const columns: ColumnDef<Employee>[] = [
{ key: 'id', title: 'ID', width: 60 },
{ key: 'name', title: 'Name', width: 150 },
{ key: 'role', title: 'Role', width: 150, editable: true },
{ key: 'salary', title: 'Salary', width: 120, sortable: true },
];
const rows: Employee[] = [
{ id: 1, name: 'Alice', role: 'Engineer', salary: 95000 },
{ id: 2, name: 'Bob', role: 'Designer', salary: 88000 },
{ id: 3, name: 'Carol', role: 'Manager', salary: 105000 },
];
function App() {
const [data, setData] = useState(rows);
const handleCellEdit = (rowIndex: number, columnKey: string, newValue: unknown) => {
setData((prev) =>
prev.map((row, i) => (i === rowIndex ? { ...row, [columnKey]: newValue } : row)),
);
};
return (
<DataGrid
columns={columns}
rows={data}
rowKey="id"
onCellEdit={handleCellEdit}
/>
);
}API Reference
<DataGrid<TRow>>
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| columns | ColumnDef<TRow>[] | required | Column definitions array |
| rows | TRow[] | required | Row data array |
| rowKey | string & keyof TRow | 'id' | Unique key field on each row for React keys |
| onCellEdit | (rowIndex, columnKey, newValue, row) => void | — | Called when a cell value is committed (Enter / blur) |
| onCellChange | (rowIndex, columnKey, newValue, row) => void | — | Called on every keystroke while editing |
| sort | SortState | — | Controlled sort state ({ columnKey, direction }) |
| onSortChange | (sort: SortState) => void | — | Callback when sort changes |
| showColumnLetters | boolean | false | Show Excel-like column letter row (A, B, C...) |
| onFreezeChange | (columnKey, frozen) => void | — | Enables freeze checkboxes in headers. Called when toggled |
| className | string | — | CSS class for the outermost wrapper |
| style | CSSProperties | — | Inline style for the outermost wrapper |
ColumnDef<TRow>
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| key | string & keyof TRow | required | Unique key matching a property on TRow |
| title | string | required | Display label in the header |
| width | number | auto | Column width in pixels |
| sortable | boolean | true | Whether this column is sortable |
| editable | boolean | false | Whether cells in this column are editable |
| frozen | boolean | false | Pin this column to the left during horizontal scroll |
| render | (value, row, rowIndex) => ReactNode | — | Custom cell renderer |
| cellStyle | (value, row, rowIndex) => CSSProperties | — | Dynamic cell style based on value |
| headerStyle | CSSProperties | — | Custom header style |
| headerClassName | string | — | Custom header CSS class |
| autoCompleteOptions | string[] | — | Autocomplete suggestions shown while editing |
SortState
interface SortState {
columnKey: string;
direction: 'asc' | 'desc' | null;
}Advanced Usage
Frozen columns
Pass onFreezeChange to enable freeze checkboxes in each column header. Set frozen: true on column definitions for initially frozen columns.
const [frozenKeys, setFrozenKeys] = useState(new Set(['id', 'name']));
const columns = allColumns.map((col) => ({
...col,
frozen: frozenKeys.has(col.key),
}));
<DataGrid
columns={columns}
rows={data}
onFreezeChange={(key, frozen) => {
setFrozenKeys((prev) => {
const next = new Set(prev);
frozen ? next.add(key) : next.delete(key);
return next;
});
}}
/>Conditional cell styling
Use cellStyle to apply dynamic styles based on cell values — useful for status indicators, heatmaps, or validation highlighting.
const columns: ColumnDef<Row>[] = [
{
key: 'status',
title: 'Status',
cellStyle: (value) => {
if (value === 'Active') return { backgroundColor: '#52c41a', color: '#fff' };
if (value === 'Pending') return { backgroundColor: '#faad14', color: '#000' };
return undefined;
},
},
];Autocomplete editing
Provide autoCompleteOptions on a column to show filtered suggestions while editing. Combine with onCellEdit to dynamically update the options list when new values are entered.
const statusOptions = ['Ready', 'In-progress', 'Blocked', 'Done'];
const columns: ColumnDef<Row>[] = [
{
key: 'status',
title: 'Status',
editable: true,
autoCompleteOptions: statusOptions,
},
];Nested data sources
For API responses with nested structures, flatten the data before passing it to the grid:
interface ApiResponse {
id: number;
name: string;
details: { connectionId: string; provider: Record<string, string> }[];
}
function flatten(data: ApiResponse[]): FlatRow[] {
const rows: FlatRow[] = [];
for (const item of data) {
for (const detail of item.details) {
rows.push({
id: `${item.id}-${detail.connectionId}`,
name: item.name,
connectionId: detail.connectionId,
...detail.provider,
});
}
}
return rows;
}Development
# Install dependencies
npm install
# Start the demo dev server
npm run dev
# Type-check
npm run typecheck
# Build the library (ESM + CJS + types)
npm run buildLicense
MIT
