Skip to main content

Table

A flexible data table component with built-in sorting, multiple visual variants, custom cell rendering, row click handling, and animated row transitions powered by Framer Motion.

Live Preview

Interact with the table component in real time. Toggle variant, hoverable, and dense mode to see changes instantly.

NameEmailRoleStatus
Sarah Chensarah.chen@company.comEngineering LeadActive
Marcus Johnsonmarcus.j@company.comProduct DesignerActive
Aisha Patelaisha.p@company.comBackend DeveloperOn Leave
David Kimdavid.kim@company.comDevOps EngineerActive
Elena Rodriguezelena.r@company.comFrontend DeveloperRemote
tsx
1<Table2columns={columns}3data={employees}4variant="default"5hoverable={true}6dense={false}7/>

Installation

Install the Aidash Components package using your preferred package manager.

$ pnpm add @aidash/components

Import

Import the Table component and its type definitions.

tsx
import { Table } from '@/components/aidash/table';
import type { TableColumn, TableProps } from '@/components/aidash/table';

Basic Usage

The simplest way to render a data table with columns and rows.

NameEmailRoleStatus
Sarah Chensarah.chen@company.comEngineering LeadActive
Marcus Johnsonmarcus.j@company.comProduct DesignerActive
Aisha Patelaisha.p@company.comBackend DeveloperOn Leave
David Kimdavid.kim@company.comDevOps EngineerActive
Elena Rodriguezelena.r@company.comFrontend DeveloperRemote
tsx
1const columns = [2{ key: 'name', header: 'Name' },3{ key: 'email', header: 'Email' },4{ key: 'role', header: 'Role' },5{ key: 'status', header: 'Status' },6];7 8import { HugeiconsIcon } from '@hugeicons/react';9import { Tag01Icon, ArchiveIcon, ArrowDataTransferHorizontalIcon } from '@hugeicons/core-free-icons';10const data = [11{ name: 'Sarah Chen', email: 'sarah.chen@company.com', role: 'Engineering Lead', status: 'Active' },12{ name: 'Marcus Johnson', email: 'marcus.j@company.com', role: 'Product Designer', status: 'Active' },13{ name: 'Aisha Patel', email: 'aisha.p@company.com', role: 'Backend Developer', status: 'On Leave' },14{ name: 'David Kim', email: 'david.kim@company.com', role: 'DevOps Engineer', status: 'Active' },15{ name: 'Elena Rodriguez', email: 'elena.r@company.com', role: 'Frontend Developer', status: 'Remote' },16];17 18<Table columns={columns} data={data} />

Variants

Three visual variants for different table aesthetics and contexts.

default

NameRoleStatus
Sarah ChenEngineering LeadActive
Marcus JohnsonProduct DesignerActive
Aisha PatelBackend DeveloperOn Leave

striped

NameRoleStatus
Sarah ChenEngineering LeadActive
Marcus JohnsonProduct DesignerActive
Aisha PatelBackend DeveloperOn Leave

bordered

NameRoleStatus
Sarah ChenEngineering LeadActive
Marcus JohnsonProduct DesignerActive
Aisha PatelBackend DeveloperOn Leave
Variants
tsx
{/* Default — clean minimal style */}
<Table columns={columns} data={data} variant="default" />

{/* Striped — alternating row backgrounds */}
<Table columns={columns} data={data} variant="striped" />

{/* Bordered — full border on all sides */}
<Table columns={columns} data={data} variant="bordered" />

Dense Mode

Compact table layout with reduced padding, ideal for data-heavy views.

Normal

NameRoleDepartment
Sarah ChenEngineering LeadEngineering
Marcus JohnsonProduct DesignerDesign
Aisha PatelBackend DeveloperEngineering

Dense

NameRoleDepartment
Sarah ChenEngineering LeadEngineering
Marcus JohnsonProduct DesignerDesign
Aisha PatelBackend DeveloperEngineering
Dense Mode
tsx
{/* Normal padding */}
<Table columns={columns} data={data} />

{/* Compact padding */}
<Table columns={columns} data={data} dense />

Sortable Columns

Enable built-in sorting by clicking column headers. Cycles through ascending, descending, and unsorted states.

Sarah Chensarah.chen@company.comEngineering LeadEngineering
Marcus Johnsonmarcus.j@company.comProduct DesignerDesign
Aisha Patelaisha.p@company.comBackend DeveloperEngineering
David Kimdavid.kim@company.comDevOps EngineerInfrastructure
Elena Rodriguezelena.r@company.comFrontend DeveloperEngineering
tsx
1const columns = [2{ key: 'name', header: 'Name', sortable: true },3{ key: 'email', header: 'Email', sortable: true },4{ key: 'role', header: 'Role', sortable: true },5{ key: 'department', header: 'Department', sortable: true },6];7 8<Table columns={columns} data={data} variant="striped" />
Tip
Click a sortable column header once for ascending, again for descending, and a third time to clear the sort. The sort indicator icons show the current direction.

Custom Cell Rendering

Use the render prop on columns to display avatars, badges, status indicators, and any custom content.

EmployeeRoleDepartmentStatus
SC

Sarah Chen

sarah.chen@company.com

Engineering LeadEngineeringActive
MJ

Marcus Johnson

marcus.j@company.com

Product DesignerDesignActive
AP

Aisha Patel

aisha.p@company.com

Backend DeveloperEngineeringOn Leave
DK

David Kim

david.kim@company.com

DevOps EngineerInfrastructureActive
ER

Elena Rodriguez

elena.r@company.com

Frontend DeveloperEngineeringRemote
tsx
1const columns = [2{3  key: 'name',4  header: 'Employee',5  render: (value, row) => (6    <div className="flex items-center gap-3">7      <div className="w-8 h-8 rounded-full bg-brand-500/15 ...">8        {value.split(' ').map(n => n[0]).join('')}9      </div>10      <div>11        <p className="font-medium">{value}</p>12        <p className="text-xs text-muted">{row.email}</p>13      </div>14    </div>15  ),16},17{ key: 'role', header: 'Role' },18{19  key: 'status',20  header: 'Status',21  render: (value) => (22    <span className="inline-flex items-center gap-1.5 px-2.5 py-0.5 ...">23      <span className="w-1.5 h-1.5 rounded-full ..." />24      {value}25    </span>26  ),27},28];29 30<Table columns={columns} data={employees} />

Row Click Handler

Add interactive row click behavior with the onRowClick callback. Rows show a pointer cursor automatically.

NameRoleDepartment
Sarah ChenEngineering LeadEngineering
Marcus JohnsonProduct DesignerDesign
Aisha PatelBackend DeveloperEngineering
David KimDevOps EngineerInfrastructure
Elena RodriguezFrontend DeveloperEngineering
tsx
1const [selected, setSelected] = useState(null);2 3<Table4columns={columns}5data={employees}6onRowClick={(row, index) => {7  setSelected(row.name);8  console.log('Clicked row', index, row);9}}10/>

Custom Column Alignment

Align column content to center or right for numeric data, prices, and status fields.

IDProduct NameCategory
PRD-001Wireless HeadphonesElectronics$89.99142
PRD-002Ergonomic KeyboardPeripherals$149.0038
PRD-003USB-C HubAccessories$59.99Out of stock
PRD-0044K MonitorDisplays$499.9912
PRD-005Webcam ProPeripherals$129.9967
Column Alignment
tsx
const columns = [
{ key: 'id', header: 'ID', width: '100px' },
{ key: 'name', header: 'Product Name' },
{ key: 'category', header: 'Category', align: 'center' },
{
  key: 'price',
  header: 'Price',
  align: 'right',
  sortable: true,
  render: (value) => `$${value.toFixed(2)}`,
},
{
  key: 'stock',
  header: 'Stock',
  align: 'right',
  sortable: true,
  render: (value) => (
    <span className={value === 0 ? 'text-danger-500' : ''}>
      {value === 0 ? 'Out of stock' : value}
    </span>
  ),
},
];

<Table columns={columns} data={products} variant="bordered" />

Row Actions

Collapse per-row actions into a dropdown menu and pin the column to the right edge, so the actions stay reachable while the rest of the table scrolls horizontally.

NameEmailRoleDepartmentStatusActions
Sarah Chensarah.chen@company.comEngineering LeadEngineeringActive
Marcus Johnsonmarcus.j@company.comProduct DesignerDesignActive
Aisha Patelaisha.p@company.comBackend DeveloperEngineeringOn Leave
David Kimdavid.kim@company.comDevOps EngineerInfrastructureActive
Elena Rodriguezelena.r@company.comFrontend DeveloperEngineeringRemote

Scroll the table sideways — the actions column stays pinned and picks up a divider once content passes beneath it.

tsx
1import { Table, TableRowActions } from '@/components/aidash/table';2 3const columns = [4{ key: 'name', header: 'Name' },5{ key: 'email', header: 'Email' },6{ key: 'role', header: 'Role' },7{ key: 'department', header: 'Department' },8{ key: 'status', header: 'Status' },9{10  key: 'actions',11  header: 'Actions',12  align: 'right',13  width: '72px',14  sticky: 'right',          // stays put while the table scrolls sideways15  render: (_value, row) => (16    <TableRowActions17      label={`Actions for ${row.name}`}18      items={[19        { label: 'View details', icon: <HugeiconsIcon icon={EyeIcon} className="w-3.5 h-3.5" />, onClick: () => view(row) },20        { label: 'Edit', icon: <HugeiconsIcon icon={PencilEdit01Icon} className="w-3.5 h-3.5" />, onClick: () => edit(row) },21        { label: 'Duplicate', icon: <HugeiconsIcon icon={Copy01Icon} className="w-3.5 h-3.5" />, onClick: () => duplicate(row) },22        { separator: true, label: '' },23        { label: 'Delete', icon: <HugeiconsIcon icon={Delete01Icon} className="w-3.5 h-3.5" />, danger: true, onClick: () => remove(row) },24      ]}25    />26  ),27},28];29 30<Table columns={columns} data={employees} />
Tip
Pin one column per side. A pinned column needs a fixed width so it does not collapse, and TableRowActions renders its menu in a portal so the dropdown is never clipped by the table's scroll container.

Empty State

When the data array is empty, the table displays a customizable message.

Default Message

NameEmailRole
No data available

Custom Message

NameEmailRole
No employees found. Try adjusting your search filters.
Empty State
tsx
{/* Default empty message */}
<Table columns={columns} data={[]} />

{/* Custom empty message */}
<Table
columns={columns}
data={[]}
emptyMessage="No employees found. Try adjusting your search filters."
/>

Accessibility

Built-in accessibility features for inclusive data table experiences.

Semantic HTML

Uses native <table>, <thead>, <tbody>, <th>, and <td> elements. Screen readers can navigate rows and columns using table navigation shortcuts.

Keyboard Navigation

Sortable column headers are focusable and can be activated with keyboard. Clickable rows respond to click events from keyboard activation on focused elements.

Visual Sort Indicators

Sortable columns display directional arrow icons that clearly indicate the current sort state (ascending, descending, or neutral), providing visual feedback alongside the data reordering.

Motion Preferences

Row animations are powered by Framer Motion which respects the prefers-reduced-motion media query. Users who prefer reduced motion will see instant transitions.

Responsive Overflow

The table wrapper uses overflow-x-auto to ensure tables remain accessible on small screens without breaking the page layout.

API Reference

Complete list of props accepted by Table and TableColumn.

TableProps<T>
PropTypeDefaultDescription
columnsTableColumn<T>[]Array of column definitions
dataT[]Array of row data objects
variant'default' | 'striped' | 'bordered''default'Visual style of the table
hoverablebooleantrueEnable row hover highlight
densebooleanfalseCompact padding for rows and cells
classNamestring''Additional CSS classes for the wrapper
emptyMessagestring'No data available'Message shown when data array is empty
onRowClick(row: T, index: number) => voidCallback fired when a row is clicked
TableColumn<T>
PropTypeDefaultDescription
keystringProperty key to read from each row object
headerstringColumn header label text
sortablebooleanfalseEnable sorting for this column
widthstringCSS width value for the column (e.g. "200px", "30%")
align'left' | 'center' | 'right''left'Text alignment for header and cells
sticky'left' | 'right'Pin the column to an edge while the table scrolls horizontally — one column per side
render(value, row, index) => ReactNodeCustom render function for cell content
Note
The generic type T extends Record<string, unknown>. TypeScript will infer the type from your data array, giving you type safety in column key and render props.

Other components that work well alongside Table.