Dice UI
Components

Data Grid

A high-performance editable data grid component with virtualization, keyboard navigation, and comprehensive cell editing capabilities.

DocsAPI
"use client";
 
import { faker } from "@faker-js/faker";
import type { ColumnDef } from "@tanstack/react-table";
import * as React from "react";
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";
 
interface SkateTrick {
  id: string;
  trickName?: string;
  skaterName?: string;
  difficulty?: "beginner" | "intermediate" | "advanced" | "expert";
  variant?: "flip" | "grind" | "grab" | "transition" | "manual" | "slide";
  landed?: boolean;
  attempts?: number;
  bestScore?: number;
  location?: string;
  dateAttempted?: string;
}
 
const skateSpots = [
  "Venice Beach Skate Park",
  "Burnside Skate Park",
  "Love Park (Philadelphia)",
  "MACBA (Barcelona)",
  "Southbank (London)",
  "FDR Skate Park",
  "Brooklyn Banks",
  "El Toro High School",
  "Hubba Hideout",
  "Wallenberg High School",
  "EMB (Embarcadero)",
  "Pier 7 (San Francisco)",
] as const;
 
const skateTricks = {
  flip: [
    "Kickflip",
    "Heelflip",
    "Tre Flip",
    "Hardflip",
    "Inward Heelflip",
    "Frontside Flip",
    "Backside Flip",
    "Varial Flip",
    "Varial Heelflip",
    "Double Flip",
    "Laser Flip",
    "Anti-Casper Flip",
    "Casper Flip",
    "Impossible",
    "360 Flip",
    "Big Spin",
    "Bigspin Flip",
  ],
  grind: [
    "50-50 Grind",
    "5-0 Grind",
    "Nosegrind",
    "Crooked Grind",
    "Feeble Grind",
    "Smith Grind",
    "Lipslide",
    "Boardslide",
    "Tailslide",
    "Noseslide",
    "Bluntslide",
    "Nollie Backside Lipslide",
    "Switch Frontside Boardslide",
  ],
  grab: [
    "Indy Grab",
    "Melon Grab",
    "Stalefish",
    "Tail Grab",
    "Nose Grab",
    "Method",
    "Mute Grab",
    "Crail Grab",
    "Seatbelt Grab",
    "Roast Beef",
    "Chicken Wing",
    "Tweaked Indy",
    "Japan Air",
  ],
  transition: [
    "Frontside Air",
    "Backside Air",
    "McTwist",
    "540",
    "720",
    "900",
    "Frontside 180",
    "Backside 180",
    "Frontside 360",
    "Backside 360",
    "Alley-Oop",
    "Fakie",
    "Revert",
    "Carve",
    "Pump",
    "Drop In",
  ],
  manual: [
    "Manual",
    "Nose Manual",
    "Casper",
    "Rail Stand",
    "Pogo",
    "Handstand",
    "One Foot Manual",
    "Spacewalk",
    "Truckstand",
    "Primo",
  ],
  slide: [
    "Powerslide",
    "Bert Slide",
    "Coleman Slide",
    "Pendulum Slide",
    "Stand-up Slide",
    "Toeside Slide",
    "Heelside Slide",
  ],
} as const;
 
function generateTrickData(): SkateTrick[] {
  return Array.from({ length: 30 }, () => {
    const variant = faker.helpers.arrayElement(
      Object.keys(skateTricks) as Array<keyof typeof skateTricks>,
    );
    const trickName = faker.helpers.arrayElement(skateTricks[variant]);
    const skaterName = faker.person.fullName();
    const attempts = faker.number.int({ min: 1, max: 50 });
    const landed = faker.datatype.boolean(0.6);
 
    const getDifficulty = (trick: string): SkateTrick["difficulty"] => {
      const expertTricks = [
        "Tre Flip",
        "900",
        "McTwist",
        "Laser Flip",
        "Impossible",
      ];
      const advancedTricks = [
        "Hardflip",
        "720",
        "540",
        "Crooked Grind",
        "Switch Frontside Boardslide",
      ];
      const intermediateTricks = [
        "Kickflip",
        "Heelflip",
        "Frontside 180",
        "50-50 Grind",
        "Boardslide",
      ];
 
      if (expertTricks.some((t) => trick.includes(t))) return "expert";
      if (advancedTricks.some((t) => trick.includes(t))) return "advanced";
      if (intermediateTricks.some((t) => trick.includes(t)))
        return "intermediate";
      return "beginner";
    };
 
    const difficulty = getDifficulty(trickName);
 
    return {
      id: faker.string.nanoid(),
      trickName,
      skaterName,
      difficulty,
      variant,
      landed,
      attempts,
      bestScore: landed
        ? faker.number.int({ min: 6, max: 10 })
        : faker.number.int({ min: 1, max: 5 }),
      location: faker.helpers.arrayElement(skateSpots),
      dateAttempted:
        faker.date
          .between({
            from: new Date(2023, 0, 1),
            to: new Date(),
          })
          .toISOString()
          .split("T")[0] ?? "",
    };
  });
}
 
export function DataGridDemo() {
  const [data, setData] = React.useState<SkateTrick[]>(generateTrickData());
 
  const columns = React.useMemo<ColumnDef<SkateTrick>[]>(
    () => [
      {
        id: "trickName",
        accessorKey: "trickName",
        header: "Trick name",
        meta: {
          cell: {
            variant: "short-text",
          },
        },
        minSize: 180,
      },
      {
        id: "skaterName",
        accessorKey: "skaterName",
        header: "Skater",
        meta: {
          cell: {
            variant: "short-text",
          },
        },
        minSize: 150,
      },
      {
        id: "difficulty",
        accessorKey: "difficulty",
        header: "Difficulty",
        meta: {
          cell: {
            variant: "select",
            options: [
              { label: "Beginner", value: "beginner" },
              { label: "Intermediate", value: "intermediate" },
              { label: "Advanced", value: "advanced" },
              { label: "Expert", value: "expert" },
            ],
          },
        },
        minSize: 120,
      },
      {
        id: "variant",
        accessorKey: "variant",
        header: "Category",
        meta: {
          cell: {
            variant: "select",
            options: [
              { label: "Flip", value: "flip" },
              { label: "Grind", value: "grind" },
              { label: "Grab", value: "grab" },
              { label: "Transition", value: "transition" },
              { label: "Manual", value: "manual" },
              { label: "Slide", value: "slide" },
            ],
          },
        },
        minSize: 120,
      },
      {
        id: "landed",
        accessorKey: "landed",
        header: "Landed",
        meta: {
          cell: {
            variant: "checkbox",
          },
        },
        minSize: 100,
      },
      {
        id: "attempts",
        accessorKey: "attempts",
        header: "Attempts",
        meta: {
          cell: {
            variant: "number",
            min: 1,
            max: 100,
          },
        },
        minSize: 100,
      },
      {
        id: "bestScore",
        accessorKey: "bestScore",
        header: "Score",
        meta: {
          cell: {
            variant: "number",
            min: 1,
            max: 10,
          },
        },
        minSize: 110,
      },
      {
        id: "location",
        accessorKey: "location",
        header: "Location",
        meta: {
          cell: {
            variant: "select",
            options: skateSpots.map((spot) => ({ label: spot, value: spot })),
          },
        },
        minSize: 180,
      },
      {
        id: "dateAttempted",
        accessorKey: "dateAttempted",
        header: "Attempted at",
        meta: {
          cell: {
            variant: "date",
          },
        },
        minSize: 130,
      },
    ],
    [],
  );
 
  const onRowAdd = React.useCallback(() => {
    setData((prev) => [...prev, { id: faker.string.nanoid() }]);
 
    return {
      rowIndex: data.length,
      columnId: "trickName",
    };
  }, [data.length]);
 
  const { table, ...dataGridProps } = useDataGrid({
    columns,
    data,
    onDataChange: setData,
    onRowAdd,
    getRowId: (row) => row.id,
    initialState: {
      columnPinning: {
        left: ["select"],
      },
    },
    enableSearch: true,
    enablePaste: true,
  });
 
  return (
    <>
      <DataGridKeyboardShortcuts enableSearch={!!dataGridProps.searchState} />
      <DataGrid {...dataGridProps} table={table} height={340} />
    </>
  );
}

Installation

Install the main component and dependencies:

npx shadcn@latest add "@diceui/data-grid"

Install optional menu components:

npm install # Sort menu with drag-and-drop reordering
npx shadcn@latest add "@diceui/data-grid-sort-menu"

# Filter menu with advanced filtering
npx shadcn@latest add "@diceui/data-grid-filter-menu"

# Row height adjustment menu
npx shadcn@latest add "@diceui/data-grid-row-height-menu"

# Column visibility menu
npx shadcn@latest add "@diceui/data-grid-view-menu"

# Keyboard shortcuts dialog
npx shadcn@latest add "@diceui/data-grid-keyboard-shortcuts"

Update import paths for custom components:

The shadcn CLI doesn't handle custom component paths properly (see issue). You'll need to update these imports manually:

In lib/data-grid.ts:

lib/data-grid.ts
import type { 
  CellPosition, 
  Direction, 
  FileCellData, 
  RowHeightValue, 
} from "@/components/data-grid/data-grid"; 
import type { 
  CellPosition, 
  Direction, 
  FileCellData, 
  RowHeightValue, 
} from "@/types/data-grid"; 

In hooks/use-data-grid.ts:

hooks/use-data-grid.ts
import { 
  getCellKey, 
  getIsFileCellData, 
  getIsInPopover, 
  getRowHeightValue, 
  getScrollDirection, 
  matchSelectOption, 
  parseCellKey, 
  scrollCellIntoView, 
} from "@/components/data-grid/data-grid"; 
import type { 
  CellPosition, 
  ContextMenuState, 
  Direction, 
  FileCellData, 
  NavigationDirection, 
  PasteDialogState, 
  RowHeightValue, 
  SearchState, 
  SelectionState, 
  UpdateCell, 
} from "@/components/data-grid/data-grid"; 
import { 
  getCellKey, 
  getIsFileCellData, 
  getIsInPopover, 
  getRowHeightValue, 
  getScrollDirection, 
  matchSelectOption, 
  parseCellKey, 
  scrollCellIntoView, 
} from "@/lib/data-grid"; 
import type { 
  CellPosition, 
  ContextMenuState, 
  Direction, 
  FileCellData, 
  NavigationDirection, 
  PasteDialogState, 
  RowHeightValue, 
  SearchState, 
  SelectionState, 
  UpdateCell, 
} from "@/types/data-grid"; 

In all components/data-grid/*.tsx files:

Update imports to use @/lib/data-grid for utility functions and @/types/data-grid for types instead of importing from @/components/data-grid/data-grid.

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: {
        cell: {
          variant: "short-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 enableSearch={!!dataGridProps.searchState} />
      <DataGrid table={table} {...dataGridProps} />
    </div>
  );
}

With Row Management

Add and delete rows with callbacks:

const onRowAdd = React.useCallback(() => {
  setData((prev) => [...prev, { id: generateId() }]);
  
  return {
    rowIndex: data.length,
    columnId: "name", // Focus this column after creating the new row
  };
}, [data.length]);

const onRowsDelete = React.useCallback((rows, rowIndices) => {
  // rows: array of row data objects
  // rowIndices: array of row indices
  setData((prev) => prev.filter((row) => !rows.includes(row)));
}, []);

const { table, ...dataGridProps } = useDataGrid({
  data,
  columns,
  onDataChange: setData,
  onRowAdd,
  onRowsDelete,
  getRowId: (row) => row.id,
});

Use the enableSearch prop to enable search functionality:

const { table, ...dataGridProps } = useDataGrid({
  data,
  columns,
  onDataChange: setData,
  enableSearch: true, // Enable search (Ctrl/Cmd+F)
});

// Pass search state to keyboard shortcuts for proper shortcuts display
<DataGridKeyboardShortcuts enableSearch={!!dataGridProps.searchState} />

With Paste Support

Use the enablePaste prop to enable pasting from clipboard:

const { table, ...dataGridProps } = useDataGrid({
  data,
  columns,
  onDataChange: setData,
  enablePaste: true, // Enable paste (Ctrl/Cmd+V)
  onRowsAdd: async (count) => {
    // Called when paste needs to add new rows
    // This is more performant than adding rows one by one with the `onRowAdd` prop
    const newRows = Array.from({ length: count }, () => ({ id: generateId() }));
    setData((prev) => [...prev, ...newRows]);
  },
});

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.

import { DirectionProvider } from "@radix-ui/react-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: { rowIndex: 0, 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
/>

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: {
    cell: {
      variant: "short-text",
    },
  },
}

Long Text Cell

Multi-line text displayed in a popover with auto-save:

{
  id: "notes",
  accessorKey: "notes",
  header: "Notes",
  meta: {
    cell: {
      variant: "long-text",
    },
  },
}

Number Cell

Numeric input with optional constraints:

{
  id: "price",
  accessorKey: "price",
  header: "Price",
  meta: {
    cell: {
      variant: "number",
      min: 0,
      max: 1000,
      step: 0.01,
    },
  },
}

URL Cell

URL input with validation and clickable links:

{
  id: "website",
  accessorKey: "website",
  header: "Website",
  meta: {
    cell: {
      variant: "url",
    },
  },
}

Checkbox Cell

Boolean checkbox for true/false values:

{
  id: "isActive",
  accessorKey: "isActive",
  header: "Active",
  meta: {
    cell: {
      variant: "checkbox",
    },
  },
}

Select Cell

Single-select input with predefined options:

{
  id: "category",
  accessorKey: "category",
  header: "Category",
  meta: {
    cell: {
      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: {
    cell: {
      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: {
    cell: {
      variant: "date",
    },
  },
}

File Cell

File upload with support for multiple files and file management:

{
  id: "attachments",
  accessorKey: "attachments",
  header: "Attachments",
  meta: {
    cell: {
      variant: "file",
      maxFileSize: 10 * 1024 * 1024, // 10MB
      maxFiles: 5,
      accept: "image/*,video/*,audio/*,.pdf,.doc,.docx",
      multiple: true,
    },
  },
}

To use file cells, provide upload and delete handlers:

const onFilesUpload = React.useCallback(async ({ files, rowIndex, 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 onFilesDelete = React.useCallback(async ({ fileIds, rowIndex, columnId }) => {
  // Delete files from your server/storage
  await fetch('/api/files', {
    method: 'DELETE',
    body: JSON.stringify({ fileIds })
  });
}, []);

const { table, ...dataGridProps } = useDataGrid({
  data,
  columns,
  onFilesUpload,
  onFilesDelete,
});

Row Selection

Add a selection column to enable row selection with shift-click support:

import { Checkbox } from "@/components/ui/checkbox";

const columns = [
  {
    id: "select",
    header: ({ table }) => (
      <Checkbox
        checked={
          table.getIsAllPageRowsSelected() ||
          (table.getIsSomePageRowsSelected() && "indeterminate")
        }
        onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
      />
    ),
    cell: ({ row, table }) => (
      <Checkbox
        checked={row.getIsSelected()}
        onCheckedChange={(value) => {
          const onRowSelect = table.options.meta?.onRowSelect;
          if (onRowSelect) {
            onRowSelect(row.index, !!value, false);
          } else {
            row.toggleSelected(!!value);
          }
        }}
        onClick={(event) => {
          if (event.shiftKey) {
            event.preventDefault();
            const onRowSelect = table.options.meta?.onRowSelect;
            if (onRowSelect) {
              onRowSelect(row.index, !row.getIsSelected(), true);
            }
          }
        }}
      />
    ),
    size: 40,
    enableSorting: false,
    enableHiding: false,
    enableResizing: false,
  },
  // ... other columns
];

The shift-click functionality is handled automatically by the useDataGrid hook when you include the onRowSelect logic.

Cell Architecture

The Data Grid uses a three-layer cell composition pattern:

  1. DataGridCell: Routes to the appropriate cell variant based on the column's meta.cell.variant property
  2. Cell Variants: Implement specific editing UIs for different data variants (text, number, select, etc.)
  3. DataGridCellWrapper: Provides common functionality for all cells (focus, selection, keyboard interactions)
// Cell composition flow
<DataGridCell cell={cell} table={table} />

<ShortTextCell {...props} />  // Based on variant

<DataGridCellWrapper {...props}>
  {/* Cell-specific content */}
</DataGridCellWrapper>

Each cell variant receives the same props and wraps its content in DataGridCellWrapper, which 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 DataGridCellWrapper:

import { DataGridCellWrapper } from "@/components/data-grid/data-grid-cell-wrapper";
import type { DataGridCellProps } from "@/types/data-grid";

export function CustomCell<TData>({
  cell,
  tableMeta,
  rowIndex,
  columnId,
  isFocused,
  isEditing,
  isSelected,
  isSearchMatch,
  isActiveSearchMatch,
  readOnly,
}: DataGridCellProps<TData>) {
  const value = cell.getValue() as CustomCellValue;
  
  return (
    <DataGridCellWrapper
      cell={cell}
      tableMeta={tableMeta}
      rowIndex={rowIndex}
      columnId={columnId}
      isEditing={isEditing}
      isFocused={isFocused}
      isSelected={isSelected}
      isSearchMatch={isSearchMatch}
      isActiveSearchMatch={isActiveSearchMatch}
      readOnly={readOnly}
    >
      {/* Your custom cell content */}
    </DataGridCellWrapper>
  );
}

Column Configuration

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<YourDataType>();
  
  return [
    {
      id: "name",
      accessorKey: "name",
      header: "Name",
      filterFn, // Enable filtering
      // Sorting is enabled by default
      meta: {
        label: "Name", // Label shown in filter/sort menus
        cell: {
          variant: "short-text",
        },
      },
    },
    // ... other columns
  ];
}, []);

Column Pinning

Pin columns to the left or right:

const { table, ...dataGridProps } = useDataGrid({
  data,
  columns,
  initialState: {
    columnPinning: {
      left: ["select", "name"], // Pin to left side
      right: ["actions"], // Pin to right 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: {
    cell: {
      variant: "short-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 onRowsDelete is provided)

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

DataGridColumnHeader

Column header with sorting controls and visual indicators for sort direction.

Prop

Type

DataGridCell

Routes to the appropriate cell variant based on the column's meta.cell.variant property.

Prop

Type

DataGridCellWrapper

Base wrapper providing common functionality for all cell variants including focus management, selection state, search highlighting, and keyboard interactions.

Prop

Type

DataGridCellVariants

Individual cell variants for different data variants. Each variant implements the DataGridCellProps interface and wraps its content in DataGridCellWrapper.

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
  • CheckboxCell: Boolean checkbox 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

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

KeyDescription
Navigate between cells
TabMove to next cell
ShiftTabMove to previous cell
HomeMove to first column
EndMove 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 + HomeMove to first cell
Ctrl + EndCmd + EndMove to last cell
PgUpMove up one page
PgDnMove down one page
Alt + ↑Scroll up one page
Alt + ↓Scroll down one page
Alt + PgUpScroll left one page of columns
Alt + PgDnScroll right one page of columns

Selection

KeyDescription
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 + ASelect all cells
Ctrl + ClickCmd + ClickToggle cell selection
Shift + ClickSelect range
EscapeClear selection or exit edit mode

Editing

KeyDescription
EnterStart editing cell
F2Start editing cell
Double ClickStart editing cell
Shift + EnterInsert row below
Ctrl + CCmd + CCopy selected cells
Ctrl + XCmd + XCut selected cells
Ctrl + VCmd + VPaste cells
DeleteClear selected cells
BackspaceClear selected cells
Ctrl + BackspaceCmd + BackspaceDelete selected rows

Search & Shortcuts

KeyDescription
Ctrl + FCmd + FOpen search
EnterNext search match (when search is open)
Shift + EnterPrevious search match (when search is open)
EscapeClose search (when search is open)
Ctrl + Shift + FCmd + Shift + FToggle filter menu
Ctrl + Shift + SCmd + Shift + SToggle 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
  • 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 with callbacks
  • 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

On this page