ObjectUIObjectUI
ComponentsComplex

Data Table

Enterprise-grade data table with sorting, filtering, pagination, and row actions

The Data Table component provides comprehensive table functionality inspired by Airtable, including multi-column sorting, search, pagination, row selection, and export capabilities.

Basic Usage

Simple Table

ID
Name
Email
Status
1
John Doe
john@example.com
Active
2
Jane Smith
jane@example.com
Active
3
Bob Johnson
bob@example.com
Inactive

With All Features

Full Featured Table

ID
Name
Role
Department
Status
Actions
1
Alice Brown
Manager
Sales
Active
2
Bob Wilson
Developer
Engineering
Active
3
Carol Davis
Designer
Design
Active
4
David Lee
Developer
Engineering
Inactive
5
Eve Martinez
Manager
Marketing
Active
Rows per page:
Page 1 of 2

Schema

interface TableColumn {
  header: string;                        // Column header text
  accessorKey: string;                   // Data property key
  width?: string;                        // Column width (e.g., '100px', '20%')
  sortable?: boolean;                    // Enable sorting for this column
}

interface DataTableSchema {
  type: 'data-table';
  
  // Structure
  columns: TableColumn[];                // Column definitions
  data: Record<string, any>[];          // Table data
  caption?: string;                      // Table caption
  
  // Features
  pagination?: boolean;                  // Enable pagination (default: true)
  pageSize?: number;                     // Items per page (default: 10)
  searchable?: boolean;                  // Enable search (default: true)
  selectable?: boolean;                  // Enable row selection (default: false)
  sortable?: boolean;                    // Enable sorting (default: true)
  exportable?: boolean;                  // Enable CSV export (default: false)
  rowActions?: boolean;                  // Show edit/delete buttons (default: false)
  
  // Advanced features
  resizableColumns?: boolean;            // Allow column resizing (default: true)
  reorderableColumns?: boolean;          // Allow column reordering (default: true)

  // Inline editing
  editable?: boolean;                    // Enable inline cell editing (default: false)
  singleClickEdit?: boolean;             // Enter edit mode on single click (default: false)
  renderCellEditor?: (ctx: {             // Host-supplied editor widget; null -> built-in input
    column: any;
    row: any;                            // the persisted record
    pendingRow: any;                     // row + this row's staged, unsaved edits (#7188)
    value: any;
    stage: (v: any) => void;
    commit: (v?: any) => void;
    cancel: () => void;
  }) => ReactNode;

  // Styling
  className?: string;                    // Tailwind CSS classes on the table wrapper
  cellClassName?: string;                // Tailwind CSS classes on the utility cells only
                                         // (select / row number / row actions)
  
  // Base properties
  id?: string;
  visible?: boolean | string | { dialect?: string; source: string };
  testId?: string;
}

Cell styling

className styles the table wrapper. Body cells have two class slots, and they reach disjoint cells — neither is a superset of the other, and no cell gets both:

  • TableColumn.cellClassName — the data cells of that one column.
  • DataTableSchema.cellClassName — the table's utility cells only: the leading selection-checkbox cell (selectable), the row-number cell (showRowNumbers), and the trailing row-actions cell (rowActions). It never reaches a data cell.

(The empty-state cell and the add-record row take neither.)

Row density is therefore a pair of settings, not one. Per-cell padding is where row height has to be expressed — height is a property of the cells, not of the row element, so rowClassName cannot express it — so a compact table sets the same density class on every column and on the schema, the second so the checkbox and row-number cells stay the same height as the data beside them. That is exactly what object-grid does for its rowHeight modes:

{
  "type": "data-table",
  "selectable": true,
  "showRowNumbers": true,
  "cellClassName": "px-2 py-1 text-sm",
  "columns": [
    { "header": "Name", "accessorKey": "name", "cellClassName": "px-2 py-1 text-sm" },
    { "header": "Amount", "accessorKey": "amount", "cellClassName": "px-2 py-1 text-sm text-right" }
  ],
  "data": [
    { "name": "Ada Lovelace", "amount": 120 },
    { "name": "Grace Hopper", "amount": 340 }
  ]
}

Drop the per-column half and the data cells stay at the table primitive's default p-4 — the schema-level key alone does not compact a row.

Inline editing

With editable: true a cell enters edit mode on double-click (or on single click with singleClickEdit: true) and the table renders one of its built-in editors — text, number, date — chosen from the column's type.

renderCellEditor lets the host supply a widget instead. The table calls it first for every cell it is about to edit; return a node to use it, or null to fall through to the built-in editor for that column. This is how object-grid gives a select or lookup cell the same dedicated control the form uses, without the component layer having to re-implement it.

The returned node is wrapped by the table so it inherits the exit-edit affordances the built-in editors have: Enter commits from a single-line input, Escape cancels, and a click outside commits. Use stage to record a value while staying in edit mode, commit to save, and cancel to discard.

The context carries the row twice, on purpose: row is the persisted record, and pendingRow is that record with the row's staged, unsaved edits merged over it (the same object as row when nothing is staged). A widget that scopes itself by a sibling field — a dependsOn lookup — should read pendingRow, so a parent edited in the same row re-scopes the child before anything is saved; row stays the place to read what the data source last returned.

Examples

Product Inventory

Inventory Management

Product Inventory
SKU
Product
Category
Stock
Price
PRD-001
Laptop Pro
Electronics
45
$1,299
PRD-002
Wireless Mouse
Accessories
120
$29
PRD-003
USB-C Cable
Accessories
200
$15
PRD-004
Monitor 27"
Electronics
30
$399
PRD-005
Keyboard
Accessories
80
$79
Rows per page:
Page 1 of 2

User Management

User Table

User
Email
Role
Last Login
Actions
John Doe
john@company.com
Admin
Jan 20, 2024
Jane Smith
jane@company.com
Editor
Jan 19, 2024
Bob Wilson
bob@company.com
Viewer
Jan 18, 2024
Alice Brown
alice@company.com
Editor
Jan 17, 2024
Rows per page:
Page 1 of 2

Sales Report

Sales Data

Q1 Sales Report
Month
Revenue
Orders
Avg Order
Growth
January
$124,500
842
$148
+12%
February
$135,200
923
$147
+8.6%
March
$148,900
1,015
$147
+10.1%

Features

The Data Table component provides:

  • Sorting: Click column headers to sort (ascending/descending/none)
  • Search: Real-time search across all columns
  • Pagination: Navigate through large datasets
  • Selection: Select multiple rows with checkboxes
  • Export: Download table data as CSV
  • Row Actions: Built-in edit and delete buttons
  • Resizable Columns: Drag column borders to resize
  • Column Reordering: Drag columns to reorder

On this page