A high-performance editable data grid component with virtualization, keyboard navigation, and comprehensive cell editing capabilities.
Installation
Install the main component and dependencies:
pnpm dlx shadcn@latest add "@diceui/data-grid"Install the following optional components:
pnpm dlx shadcn@latest add "@diceui/data-grid-select-column"pnpm dlx shadcn@latest add "@diceui/data-grid-actions-column"pnpm dlx shadcn@latest add "@diceui/data-grid-sort-menu"pnpm dlx shadcn@latest add "@diceui/data-grid-filter-menu"pnpm dlx shadcn@latest add "@diceui/data-grid-row-height-menu"pnpm dlx shadcn@latest add "@diceui/data-grid-view-menu"pnpm dlx shadcn@latest add "@diceui/data-grid-keyboard-shortcuts"pnpm dlx shadcn@latest add "@diceui/data-grid-skeleton"Usage
Basic Data Grid
import { DataGrid } from "@/components/data-grid/data-grid";
import { DataGridKeyboardShortcuts } from "@/components/data-grid/data-grid-keyboard-shortcuts";
import { useDataGrid } from "@/hooks/use-data-grid";
export default function MyDataGrid() {
const [data, setData] = React.useState(initialData);
const columns = React.useMemo(() => [
{
id: "name",
accessorKey: "name",
header: "Name",
meta: {
variant: "text",
},
},
// ... other columns
], []);
const { table, ...dataGridProps } = useDataGrid({
data,
columns,
onDataChange: setData,
getRowId: (row) => row.id,
});
return (
<>
<DataGridKeyboardShortcuts />
<DataGrid table={table} {...dataGridProps} />
</>
);
}With Toolbar Menus
Add sort, filter, row height, and view menus:
import { DataGrid } from "@/components/data-grid/data-grid";
import { DataGridFilterMenu } from "@/components/data-grid/data-grid-filter-menu";
import { DataGridKeyboardShortcuts } from "@/components/data-grid/data-grid-keyboard-shortcuts";
import { DataGridRowHeightMenu } from "@/components/data-grid/data-grid-row-height-menu";
import { DataGridSortMenu } from "@/components/data-grid/data-grid-sort-menu";
import { DataGridViewMenu } from "@/components/data-grid/data-grid-view-menu";
import { useDataGrid } from "@/hooks/use-data-grid";
export default function DataGridToolbarDemo() {
const { table, ...dataGridProps } = useDataGrid({
data,
columns,
onDataChange: setData,
});
return (
<div className="flex flex-col gap-4">
{/* Toolbar */}
<div role="toolbar" aria-orientation="horizontal" className="flex items-center gap-2 self-end">
<DataGridFilterMenu table={table} />
<DataGridSortMenu table={table} />
<DataGridRowHeightMenu table={table} />
<DataGridViewMenu table={table} />
</div>
<DataGridKeyboardShortcuts />
<DataGrid table={table} {...dataGridProps} />
</div>
);
}With Row Management
Pass createRow to let users add rows, and enableRowDeletion to let them delete them. createRow only returns the new row. The grid inserts it in the right place, focuses it, and reports it through onDataChange.
Its details.reason says where the row came from: the footer's add row button ("add-row-press"), Shift + Enter on a cell ("insert-below", with the afterRowId it goes after), or a paste that needs more rows ("paste"). Return null to skip adding a row. It can also return a promise, for example to create the row on your server first.
const createRow = React.useCallback(() => ({ id: generateId() }), []);
const { table, ...dataGridProps } = useDataGrid({
data,
columns,
onDataChange: setData,
createRow,
enableRowDeletion: true,
getRowId: (row) => row.id,
});
return (
<>
<DataGridKeyboardShortcuts enableRowCreation enableRowDeletion />
<DataGrid table={table} {...dataGridProps} />
</>
);You can also add and delete rows from your own code with table.addRows and table.deleteRows. addRows returns the new row ids and takes afterRowId or beforeRowId to place the rows:
function duplicateRows(rows: Row<DataGridFeatures, Person>[]) {
for (const row of rows) {
table.addRows([{ ...row.original, id: generateId() }], {
afterRowId: row.id,
});
}
}
table.deleteRows(["row-1", "row-2"]);Handling Data Changes
Every change to the data, whether from an edit, a paste, table.updateCells, adding or deleting rows, or undo/redo, comes out of onDataChange. It receives the next data and a details object that tells you what changed, so you can save just that:
const { table, ...dataGridProps } = useDataGrid({
data,
columns,
getRowId: (row) => row.id,
onDataChange: (nextData, { reason, changes, addedRows, removedRows }) => {
setData(nextData);
// reason: "edit" | "paste" | "update-cells" | "add-rows" | "delete-rows" | "undo" | "redo"
// changes: [{ rowId, columnId, previousValue, newValue }]
void saveChanges({ changes, addedRows, removedRows });
},
});Passing the data back is optional. The grid keeps its own copy of the data and shows every change, so you can leave out setData if you only need to save the changes. A new data prop replaces the grid's copy.
With Search
Use the enableSearch prop to enable search functionality:
const { table, ...dataGridProps } = useDataGrid({
data,
columns,
onDataChange: setData,
enableSearch: true, // Enable search (Ctrl/Cmd+F)
});
return (
<>
<DataGridKeyboardShortcuts enableSearch />
<DataGrid {...dataGridProps} table={table} />
</>
);With Paste Support
Use the enablePaste prop to enable pasting from clipboard. When a paste runs past the last row, the grid calls createRow with reason: "paste" for each extra row it needs:
const { table, ...dataGridProps } = useDataGrid({
data,
columns,
onDataChange: setData,
enablePaste: true, // Enable paste (Ctrl/Cmd+V)
createRow: () => ({ id: generateId() }),
});With Undo/Redo
Undo/redo is built in. Edits, pastes, table.updateCells calls, and added or deleted rows go into the history. Users undo with Ctrl / Cmd + Z and redo with Ctrl / Cmd + Shift + Z or Ctrl / Cmd + Y. It's on unless the grid is read-only. Set enableUndoRedo: false to turn it off, and maxHistory to keep more or fewer steps than the default 100.
Call table.undo() and table.redo() from your own buttons, and read table.getCanUndo() and table.getCanRedo() to disable them:
const { table, ...dataGridProps } = useDataGrid({
data,
columns,
onDataChange: setData,
getRowId: (row) => row.id,
});
return (
<>
<div role="toolbar" aria-orientation="horizontal" className="flex gap-2">
<Button disabled={!table.getCanUndo()} onClick={() => table.undo()}>
Undo
</Button>
<Button disabled={!table.getCanRedo()} onClick={() => table.redo()}>
Redo
</Button>
</div>
<DataGridKeyboardShortcuts enableUndoRedo />
<DataGrid table={table} {...dataGridProps} />
</>
);table.clearHistory() empties the history, for example after saving.
Updating Cells
Use table.updateCells to change values from your own code, such as server pushes or a live feed. It re-renders only the cells that changed. Pass one update or an array, and update a single cell with columnId and value, or several cells in a row with values:
table.updateCells([
{ rowId: "AAPL", columnId: "price", value: 231.4 },
{ rowId: "MSFT", values: { price: 418.2, change: -0.6 } },
]);For data that changes many times a second, pass { batch: true }. Visible cells update once a frame, and onDataChange runs at most once every batchUpdateDelay milliseconds (500 by default). Batched updates stay out of undo history unless you also pass history: true.
React.useEffect(() => {
return subscribeToPrices((updates) => {
table.updateCells(updates, { batch: true });
});
}, [table]);updateCells works on read-only grids too, so you can show live data that users can't edit.
For the fastest updates, give a column meta.writeCell. The grid then skips rendering that column's cells and calls writeCell(element, value) to write each new value straight into the cell element:
{
id: "price",
accessorKey: "price",
header: "Price",
meta: {
writeCell: (element, value) => {
element.textContent = currencyFormatter.format(value as number);
},
},
}Read-Only Mode
Use the readOnly prop to make the grid read-only:
const { table, ...dataGridProps } = useDataGrid({
data,
columns,
readOnly: true, // Disable all editing
});RTL Support
Wrap the grid in a DirectionProvider and the language direction will be automatically detected. The direction component is included when you install the data grid — see the shadcn Direction docs for the underlying API.
import { DirectionProvider } from "@/components/ui/direction";
return (
<DirectionProvider dir="rtl">
<DataGridImpl />
</DirectionProvider>
)
function DataGridImpl() {
const { table, ...dataGridProps } = useDataGrid({
data,
columns,
});
return (
<DataGrid table={table} {...dataGridProps} />
)
}Auto Focus
Use the autoFocus prop to automatically focus any navigable cell on mount:
const { table, ...dataGridProps } = useDataGrid({
data,
columns,
autoFocus: true, // Focus first navigable cell
// Or focus a specific cell:
// autoFocus: { rowId: "row-1", columnId: "name" },
});Custom Height and Stretch Columns
Control the grid height and column stretching:
<DataGrid
table={table}
{...dataGridProps}
height={800} // Custom height in pixels (default: 600)
stretchColumns={true} // Stretch columns to fill available width
/>Loading States
Use the DataGridSkeleton component for loading states:
import { Suspense } from "react";
import {
DataGridSkeleton,
DataGridSkeletonGrid,
DataGridSkeletonToolbar,
} from "@/components/data-grid/data-grid-skeleton";
export default function Page() {
return (
<Suspense
fallback={
<DataGridSkeleton className="container flex flex-col gap-4 py-4">
<DataGridSkeletonToolbar actionCount={5} />
<DataGridSkeletonGrid />
</DataGridSkeleton>
}
>
<DataGridDemo />
</Suspense>
);
}Cell Variants
The Data Grid supports various cell variants for different data formats:
Short Text Cell
Single-line text input with inline editing:
{
id: "name",
accessorKey: "name",
header: "Name",
meta: {
variant: "text",
},
}Long Text Cell
Multi-line text displayed in a popover with auto-save:
{
id: "notes",
accessorKey: "notes",
header: "Notes",
meta: {
variant: "long-text",
},
}Number Cell
Numeric input with optional constraints:
{
id: "price",
accessorKey: "price",
header: "Price",
meta: {
variant: "number",
min: 0,
max: 1000,
step: 0.01,
format: { style: "currency", currency: "USD" },
},
}format takes Intl.NumberFormat options for how the value displays. Editing still uses the plain number.
URL Cell
URL input with validation and clickable links:
{
id: "website",
accessorKey: "website",
header: "Website",
meta: {
variant: "url",
},
}Boolean Cell
Checkbox for true/false values. Set control: "switch" to render a switch instead:
{
id: "isActive",
accessorKey: "isActive",
header: "Active",
meta: {
variant: "boolean",
},
}Select Cell
Single-select input with predefined options:
{
id: "category",
accessorKey: "category",
header: "Category",
meta: {
variant: "select",
options: [
{ label: "Electronics", value: "electronics" },
{ label: "Clothing", value: "clothing" },
{ label: "Books", value: "books" },
],
},
}Multi-Select Cell
Multi-select input with predefined options and badge display:
{
id: "skills",
accessorKey: "skills",
header: "Skills",
meta: {
variant: "multi-select",
options: [
{ label: "JavaScript", value: "javascript" },
{ label: "TypeScript", value: "typescript" },
{ label: "React", value: "react" },
],
},
}Date Cell
Date picker with calendar popover:
{
id: "startDate",
accessorKey: "startDate",
header: "Start Date",
meta: {
variant: "date",
},
}File Cell
File upload with support for multiple files and file management:
{
id: "attachments",
accessorKey: "attachments",
header: "Attachments",
meta: {
variant: "file",
maxFileSize: 10 * 1024 * 1024, // 10MB
maxFiles: 5,
accept: "image/*,video/*,audio/*,.pdf,.doc,.docx",
multiple: true,
},
}To use file cells, provide uploadFiles and deleteFiles:
const uploadFiles = React.useCallback(async ({ files, rowId, columnId }) => {
// Upload files to your server/storage
const formData = new FormData();
files.forEach(file => formData.append('files', file));
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
const data = await response.json();
// Return array of file metadata
return data.files.map(f => ({
id: f.fileId,
name: f.fileName,
size: f.fileSize,
type: f.fileType,
url: f.fileUrl
}));
}, []);
const deleteFiles = React.useCallback(async ({ fileIds, rowId, columnId }) => {
// Delete files from your server/storage
await fetch('/api/files', {
method: 'DELETE',
body: JSON.stringify({ fileIds })
});
}, []);
const { table, ...dataGridProps } = useDataGrid({
data,
columns,
uploadFiles,
deleteFiles,
});Row Selection
Add a selection column to enable row selection with shift-click support using the getDataGridSelectColumn helper:
import { getDataGridSelectColumn } from "@/components/data-grid/data-grid-select-column";
const columns = [
getDataGridSelectColumn<YourData>(),
// ... other columns
];You can customize the select column by passing options:
getDataGridSelectColumn<YourData>({
size: 50, // Custom width (default: 40)
enableHiding: true, // Allow hiding via view menu (default: false)
enableResizing: true, // Allow resizing (default: false)
})The shift-click functionality is handled automatically by the useDataGrid hook when you include the select column.
Row Actions
Add a menu of actions to each row with getDataGridActionsColumn, and show bulk actions for the selection with the actionBar prop. The action bar renders while rows or a range of cells are selected.
import { getDataGridActionsColumn } from "@/components/data-grid/data-grid-actions-column";
import { getDataGridSelectColumn } from "@/components/data-grid/data-grid-select-column";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
const columns = React.useMemo(() => [
getDataGridSelectColumn(),
// ... other columns
getDataGridActionsColumn({
actions: ({ row, table }) => (
<DropdownMenuItem onClick={() => table.deleteRows([row.id])}>
Delete
</DropdownMenuItem>
),
}),
], []);
const { table, ...dataGridProps } = useDataGrid({
data,
columns,
initialState: {
columnPinning: { start: ["select"], end: ["actions"] },
},
});
return (
<DataGrid
{...dataGridProps}
table={table}
actionBar={<TasksActionBar table={table} />}
/>
);Cell Architecture
The Data Grid uses a three-layer cell composition pattern:
- DataGridDataCell: Routes to the appropriate cell variant based on the column's
meta.variantproperty - Cell Variants: Implement specific editing UIs for different data variants (text, number, select, etc.)
- DataGridCell: The cell element itself, which provides common functionality for all cells (focus, selection, keyboard interactions)
// Cell composition flow
<DataGridDataCell {...cellProps} />
↓
<ShortTextCell {...props} /> // Based on variant
↓
<DataGridCell {...props}>
{/* Cell-specific content */}
</DataGridCell>Each cell variant receives the same props and wraps its content in DataGridCell, which renders the data-slot="data-grid-cell" element and provides:
- Focus management and visual focus ring
- Selection state and highlighting
- Search match highlighting
- Click, double-click, and keyboard event management
- Edit mode triggering (Enter, F2, Space, or typing)
Creating Custom Cell Variants
You can create custom cell variants by implementing the DataGridCellProps interface and wrapping your content in DataGridCell:
import { DataGridCell } from "@/components/data-grid/data-grid-cell";
import type { DataGridCellProps } from "@/lib/data-grid-types";
export function CustomCell<TData>(props: DataGridCellProps<TData>) {
const { cell, isEditing, readOnly } = props;
const value = cell.getValue() as CustomCellValue;
function onCommit(nextValue: CustomCellValue) {
if (!readOnly) cell.setValue(nextValue);
cell.table.stopEditing();
}
return (
<DataGridCell {...props}>
{/* Your custom cell content */}
</DataGridCell>
);
}Cells read and write data through the cell, with cell.getValue() and cell.setValue(), and control edit mode with cell.startEditing() and cell.table.stopEditing().
Column Configuration
Column Icons
Each header shows an icon for its cell variant. Set meta.icon to use your own:
{
id: "price",
accessorKey: "price",
header: "Price",
meta: {
icon: DollarSignIcon,
variant: "number",
},
}Enabling Filtering and Sorting
To enable filtering and sorting on columns, use the filterFn from the data grid library:
import { getFilterFn } from "@/lib/data-grid-filters";
const columns = React.useMemo(() => {
const filterFn = getFilterFn<YourData>();
return [
{
id: "name",
accessorKey: "name",
header: "Name",
filterFn, // Enable filtering
// Sorting is enabled by default
meta: {
label: "Name", // Label shown in filter/sort menus
variant: "text",
},
},
// ... other columns
];
}, []);Column Pinning
Pin columns to the left or right:
const { table, ...dataGridProps } = useDataGrid({
data,
columns,
initialState: {
columnPinning: {
start: ["select", "name"], // Pin to the start side
end: ["actions"], // Pin to the end side
},
},
});You can also pin/unpin columns dynamically via the column header dropdown menu.
Column Resizing
Column resizing is enabled by default. Users can:
- Drag the column resize handle
- Double-click the resize handle to auto-fit the column width
Set minimum column size using minSize:
{
id: "name",
accessorKey: "name",
header: "Name",
minSize: 180, // Minimum width in pixels
meta: {
variant: "text",
},
}Context Menu Actions
Right-click on cells to access context menu options:
- Copy (Ctrl/Cmd+C): Copy selected cells to clipboard
- Cut (Ctrl/Cmd+X): Cut selected cells (shows visual indicator)
- Clear (Delete/Backspace): Clear content from selected cells
- Delete rows (Ctrl/Cmd+Backspace): Remove selected rows (only available when
enableRowDeletionis set)
API Reference
useDataGrid
Hook for initializing the data grid with state management and editing capabilities.
Prop
Type
DataGrid
Main data grid component with virtualization and editing capabilities.
Prop
Type
getDataGridSelectColumn
A utility function that returns a reusable select column definition for the data grid with checkbox selection and shift-click support.
Prop
Type
getDataGridActionsColumn
A utility function that returns an actions column with a dropdown menu for each row.
Prop
Type
DataGridColumnHeader
Column header with sorting controls and visual indicators for sort direction.
Prop
Type
DataGridCell
The cell element that all cell variants render into. It handles focus management, selection state, search highlighting, and keyboard interactions, and renders with data-slot="data-grid-cell".
Prop
Type
DataGridCellVariants
Individual cell variants for different data variants. Each variant implements the DataGridCellProps interface and wraps its content in DataGridCell.
Prop
Type
Available cell variants:
- ShortTextCell: Single-line text input with inline contentEditable
- LongTextCell: Multi-line textarea displayed in a popover dialog with auto-save
- NumberCell: Numeric input with optional min, max, and step constraints
- UrlCell: URL input with validation and clickable links
- SelectCell: Single-select dropdown with predefined options
- MultiSelectCell: Multi-select input with badge display and command palette
- BooleanCell: Checkbox or switch for true/false values
- DateCell: Date picker with calendar popover
- FileCell: File upload with support for multiple files and file management
DataGridRow
Individual row component with virtualization support for large datasets.
Prop
Type
DataGridSearch
Search menu with keyboard shortcuts for finding and navigating between matching cells in the grid.
Prop
Type
DataGridFilterMenu
Filter menu with drag-and-drop reordering and advanced filtering operators for text, number, date, select, and boolean fields.
Prop
Type
DataGridSortMenu
Sort menu with drag-and-drop reordering for multi-column sorting with ascending/descending controls.
Prop
Type
DataGridRowHeightMenu
Row height menu for adjusting row sizes between short, medium, tall, and extra-tall options.
Prop
Type
DataGridViewMenu
View menu for controlling column visibility with search functionality.
Prop
Type
DataGridContextMenu
Right-click context menu for quick access to common cell and row actions like copy, cut, clear, and delete.
Prop
Type
DataGridPasteDialog
Dialog for handling paste operations that require adding new rows to the grid.
Prop
Type
DataGridKeyboardShortcuts
Searchable reference dialog for all available keyboard shortcuts for navigating and interacting with the data grid.
Prop
Type
DataGridSkeleton
A composable skeleton component for the data grid with toolbar and grid parts for loading states.
import {
DataGridSkeleton,
DataGridSkeletonGrid,
DataGridSkeletonToolbar,
} from "@/components/data-grid/data-grid-skeleton";
return (
<DataGridSkeleton>
<DataGridSkeletonToolbar />
<DataGridSkeletonGrid />
</DataGridSkeleton>
);DataGridSkeletonToolbar
Prop
Type
DataGridSkeletonGrid
Accessibility
The Data Grid follows WAI-ARIA guidelines for grid widgets:
- Full keyboard navigation support
- Proper ARIA labels, roles, and properties
- Focus management with visible focus indicators
- Keyboard shortcuts for all actions
- Screen reader friendly cell updates
Keyboard Interactions
Navigation
| Key | Description |
|---|---|
| ↑↓←→ | Navigate between cells |
| Tab | Move to next cell |
| ShiftTab | Move to previous cell |
| Home | Move to first column |
| End | Move to last column |
| Ctrl + ↑Cmd + ↑ | Move to first row (same column) |
| Ctrl + ↓Cmd + ↓ | Move to last row (same column) |
| Ctrl + ←Cmd + ← | Move to first column (same row) |
| Ctrl + →Cmd + → | Move to last column (same row) |
| Ctrl + HomeCmd + Home | Move to first cell |
| Ctrl + EndCmd + End | Move to last cell |
| PgUp | Move up one page |
| PgDn | Move down one page |
| Alt + ↑ | Scroll up one page |
| Alt + ↓ | Scroll down one page |
| Alt + PgUp | Scroll left one page of columns |
| Alt + PgDn | Scroll right one page of columns |
Selection
| Key | Description |
|---|---|
| Shift + ↑Shift + ↓Shift + ←Shift + → | Extend selection |
| Ctrl + Shift + ↑Cmd + Shift + ↑ | Select to top of table |
| Ctrl + Shift + ↓Cmd + Shift + ↓ | Select to bottom of table |
| Ctrl + Shift + ←Cmd + Shift + ← | Select to first column |
| Ctrl + Shift + →Cmd + Shift + → | Select to last column |
| Ctrl + ACmd + A | Select all cells |
| Ctrl + ClickCmd + Click | Toggle cell selection |
| Shift + Click | Select range |
| Escape | Clear selection or exit edit mode |
Editing
| Key | Description |
|---|---|
| Enter | Start editing cell |
| F2 | Start editing cell |
| Double Click | Start editing cell |
| Shift + Enter | Insert row below |
| Ctrl + CCmd + C | Copy selected cells |
| Ctrl + XCmd + X | Cut selected cells |
| Ctrl + VCmd + V | Paste cells |
| Delete | Clear selected cells |
| Backspace | Clear selected cells |
| Ctrl + BackspaceCmd + Backspace | Delete selected rows |
| Ctrl + DeleteCmd + Delete | Delete selected rows |
| Ctrl + ZCmd + Z | Undo last action |
| Ctrl + Shift + ZCmd + Shift + Z | Redo last action |
| Ctrl + YCmd + Y | Redo last action |
Search & Shortcuts
| Key | Description |
|---|---|
| Ctrl + FCmd + F | Open search |
| Enter | Next search match (when search is open) |
| Shift + Enter | Previous search match (when search is open) |
| Escape | Close search (when search is open) |
| Ctrl + Shift + FCmd + Shift + F | Toggle filter menu |
| Ctrl + Shift + SCmd + Shift + S | Toggle sort menu |
| Ctrl + /Cmd + / | Show keyboard shortcuts |
Features
The Data Grid component provides a comprehensive spreadsheet-like experience with:
Core Features
- High Performance: Virtualized rows and columns for handling large datasets (10,000+ rows)
- Cell Editing: In-place editing with 9 different cell variants
- Cell Selection: Single and multi-cell selection with keyboard and mouse
- Keyboard Navigation: Full keyboard support with Excel-like shortcuts
- Copy/Cut/Paste: Full clipboard support including paste from Excel/Google Sheets
- Undo/Redo: Built-in history for edits, pastes, cell updates, row additions, and deletions (Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z)
- Live Updates:
table.updateCellsre-renders only the changed cells, with batching for high-frequency data - Context Menu: Right-click actions for cells and rows
- Search: Find and navigate to matching cells (Ctrl/Cmd+F)
- Filtering: Advanced filtering with multiple operators and drag-and-drop reordering
- Sorting: Multi-column sorting with drag-and-drop reordering
- Row Management: Add and delete rows from the grid or with
table.addRowsandtable.deleteRows - Column Features: Resizing, pinning (left/right), hiding, and reordering
Cell Variants
- Text Cells: Short-text and long-text with auto-save
- Number Cells: With min/max/step constraints
- URL Cells: With validation and clickable links
- Date Cells: Calendar picker with keyboard navigation
- Select Cells: Single and multi-select with search
- Checkbox Cells: Boolean values
- File Cells: Upload and manage multiple files per cell
Advanced Features
- Smart Paste: Automatically expands grid when pasting more data than fits
- Auto-Fill: Type to start editing, like in Excel
- Row Heights: Adjustable row heights (short, medium, tall, extra-tall)
- RTL Support: Full right-to-left language support
- Read-Only Mode: Lock the grid to prevent editing
- Auto-Focus: Focus specific cell on mount
- Accessibility: Full ARIA support and keyboard navigation
Credits
- TanStack Table - For the table state management.
- TanStack Virtual - For the virtualization.
- shadcn/ui - For the UI components.
- Airtable and Glide Data Grid - For accessibility and best practices.