Skip to content

Table

Feature-rich table component for displaying tabular data.

import { Table } from 'asterui'
import type { ColumnType, RowSelection, ExpandableConfig, PaginationConfig } from 'asterui'

Basic Table

Simple table with data.

Name
Email
Role
John Doejohn@example.comAdmin
Jane Smithjane@example.comUser
Bob Johnsonbob@example.comUser
Alice Williamsalice@example.comEditor
Charlie Browncharlie@example.comUser
import { Table } from 'asterui'

function App() {
  const userData = [
    { id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin' },
    { id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
    { id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'User' },
    { id: '4', name: 'Alice Williams', email: 'alice@example.com', role: 'Editor' },
    { id: '5', name: 'Charlie Brown', email: 'charlie@example.com', role: 'User' },
  ];
  
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name' },
    { key: 'email', title: 'Email', dataIndex: 'email' },
    { key: 'role', title: 'Role', dataIndex: 'role' },
  ];
  
  return (
      <Table columns={columns} dataSource={userData} pagination={false} />
    )
}

export default App

Custom Rendering

Use render functions for tags, buttons, and custom content.

Name
Email
Role
Status
Actions
John Doejohn@example.comAdminactive
Jane Smithjane@example.comUseractive
Bob Johnsonbob@example.comUserinactive
Alice Williamsalice@example.comEditoractive
import { Table, Tag, Button, Space } from 'asterui'

function App() {
  const userData = [
    { id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'active' },
    { id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User', status: 'active' },
    { id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'User', status: 'inactive' },
    { id: '4', name: 'Alice Williams', email: 'alice@example.com', role: 'Editor', status: 'active' },
  ];
  
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name', width: 150 },
    { key: 'email', title: 'Email', dataIndex: 'email' },
    { key: 'role', title: 'Role', dataIndex: 'role', align: 'center' },
    {
      key: 'status',
      title: 'Status',
      dataIndex: 'status',
      align: 'center',
      render: (value) => (
        <Tag color={value === 'active' ? 'success' : 'ghost'} size="sm">
          {String(value)}
        </Tag>
      ),
    },
    {
      key: 'actions',
      title: 'Actions',
      align: 'right',
      render: () => (
        <Space size="xs">
          <Button size="xs" variant="ghost">Edit</Button>
          <Button size="xs" variant="ghost">Delete</Button>
        </Space>
      ),
    },
  ];
  
  return (
      <Table columns={columns} dataSource={userData} pagination={false} />
    )
}

export default App

Striped Rows

Zebra striping for better readability.

Name
Email
Role
John Doejohn@example.comAdmin
Jane Smithjane@example.comUser
Bob Johnsonbob@example.comUser
Alice Williamsalice@example.comEditor
Charlie Browncharlie@example.comUser
import { Table } from 'asterui'

function App() {
  return (
      <Table columns={basicColumns} dataSource={userData.slice(0, 5)} striped pagination={false} />
    )
}

export default App

Bordered Table

Add borders for a more defined look.

Name
Email
Role
John Doejohn@example.comAdmin
Jane Smithjane@example.comUser
Bob Johnsonbob@example.comUser
Alice Williamsalice@example.comEditor
Charlie Browncharlie@example.comUser
import { Table } from 'asterui'

function App() {
  return (
      <Table columns={basicColumns} dataSource={userData.slice(0, 5)} bordered pagination={false} />
    )
}

export default App

Compact Size

Extra small size for dense data.

Name
Email
Role
John Doejohn@example.comAdmin
Jane Smithjane@example.comUser
Bob Johnsonbob@example.comUser
Alice Williamsalice@example.comEditor
Charlie Browncharlie@example.comUser
import { Table } from 'asterui'

function App() {
  return (
      <Table
        columns={basicColumns}
        dataSource={userData.slice(0, 5)}
        size="xs"
        striped
        bordered
        pagination={false}
      />
    )
}

export default App

Extra Large Size

Extra large size for emphasis.

Name
Email
Role
John Doejohn@example.comAdmin
Jane Smithjane@example.comUser
Bob Johnsonbob@example.comUser
import { Table } from 'asterui'

function App() {
  return (
      <Table columns={basicColumns} dataSource={userData.slice(0, 3)} size="xl" pagination={false} />
    )
}

export default App

Empty State

Customizable empty state message.

Name
Email
Role
No users found
import { Table } from 'asterui'

function App() {
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name' },
    { key: 'email', title: 'Email', dataIndex: 'email' },
    { key: 'role', title: 'Role', dataIndex: 'role' },
  ];
  
  return (
      <Table
        columns={columns}
        dataSource={[]}
        pagination={false}
        locale={{ emptyText: 'No users found' }}
      />
    )
}

export default App

Loading State

Show loading spinner while fetching data.

Name
Email
Role
John Doejohn@example.comAdmin
Jane Smithjane@example.comUser
Bob Johnsonbob@example.comUser
Alice Williamsalice@example.comEditor
Charlie Browncharlie@example.comUser
import { Table, Button } from 'asterui'
import { useState } from 'react'

function App() {
  const [isLoading, setIsLoading] = useState(false);
  
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name' },
    { key: 'email', title: 'Email', dataIndex: 'email' },
    { key: 'role', title: 'Role', dataIndex: 'role' },
  ];
  
  const userData = [
    { id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin' },
    { id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
    { id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'User' },
    { id: '4', name: 'Alice Williams', email: 'alice@example.com', role: 'Editor' },
    { id: '5', name: 'Charlie Brown', email: 'charlie@example.com', role: 'User' },
  ];
  
  return (
      <div className="space-y-4">
        <Button size="sm" onClick={() => setIsLoading(!isLoading)}>
          {isLoading ? 'Hide' : 'Show'} Loading
        </Button>
        <Table columns={columns} dataSource={userData} loading={isLoading} pagination={false} />
      </div>
    )
}

export default App

Basic Pagination

Paginate large datasets.

Name
Email
Role
John Doejohn@example.comAdmin
Jane Smithjane@example.comUser
Bob Johnsonbob@example.comUser
Alice Williamsalice@example.comEditor
Charlie Browncharlie@example.comUser
import { Table } from 'asterui'

function App() {
  return (
      <Table columns={basicColumns} dataSource={userData} pagination={{ pageSize: 5 }} />
    )
}

export default App

Advanced Pagination

Page size changer, quick jumper, and total display.

Name
Email
Role
John Doejohn@example.comAdmin
Jane Smithjane@example.comUser
Bob Johnsonbob@example.comUser
Alice Williamsalice@example.comEditor
Charlie Browncharlie@example.comUser
1-5 of 12 items
Go to
import { Table } from 'asterui'

function App() {
  return (
      <Table
        columns={basicColumns}
        dataSource={userData}
        pagination={{
          pageSize: 5,
          showSizeChanger: true,
          showQuickJumper: true,
          showTotal: (total, range) => `${range[0]}-${range[1]} of ${total} items`,
          pageSizeOptions: [5, 10, 20],
        }}
      />
    )
}

export default App

Pinned Header

Keep header visible while scrolling.

Name
Email
Role
John Doejohn@example.comAdmin
Jane Smithjane@example.comUser
Bob Johnsonbob@example.comUser
Alice Williamsalice@example.comEditor
Charlie Browncharlie@example.comUser
David Leedavid@example.comUser
Emma Wilsonemma@example.comEditor
Frank Millerfrank@example.comUser
Grace Taylorgrace@example.comAdmin
Henry Davishenry@example.comUser
Iris Martiniris@example.comEditor
Jack Whitejack@example.comUser
import { Table } from 'asterui'

function App() {
  return (
      <div className="max-h-64 overflow-auto border border-base-content/10 rounded-lg">
        <Table columns={basicColumns} dataSource={userData} pinRows striped pagination={false} />
      </div>
    )
}

export default App

Column Sorting

Click column headers to sort data.

Name
Age
Email
Jane Smith28jane@example.com
Charlie Brown29charlie@example.com
John Doe32john@example.com
Alice Williams35alice@example.com
David Lee41david@example.com
Bob Johnson45bob@example.com
import { Table } from 'asterui'

function App() {
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name', sorter: true },
    { key: 'age', title: 'Age', dataIndex: 'age', sorter: true, defaultSortOrder: 'ascend' },
    { key: 'email', title: 'Email', dataIndex: 'email', sorter: true },
  ];
  
  return (
      <Table
        columns={columns}
        dataSource={userData.slice(0, 6)}
        pagination={false}
      />
    )
}

export default App

Controlled Sorting

Externally control sort state.

Name
Email
Role
Alice Williamsalice@example.comEditor
Bob Johnsonbob@example.comUser
Charlie Browncharlie@example.comUser
Jane Smithjane@example.comUser
John Doejohn@example.comAdmin
import { Table, Button, Space } from 'asterui'
import { useState } from 'react'

function App() {
  const [sortOrder, setSortOrder] = useState<'ascend' | 'descend' | null>('ascend');
  
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name', sorter: true, sortOrder },
    { key: 'email', title: 'Email', dataIndex: 'email' },
    { key: 'role', title: 'Role', dataIndex: 'role' },
  ];
  
  const userData = [
    { id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin' },
    { id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
    { id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'User' },
    { id: '4', name: 'Alice Williams', email: 'alice@example.com', role: 'Editor' },
    { id: '5', name: 'Charlie Brown', email: 'charlie@example.com', role: 'User' },
  ];
  
  return (
      <div className="space-y-4">
        <Space size="sm">
          <Button size="sm" variant={sortOrder === 'ascend' ? 'primary' : 'outline'} onClick={() => setSortOrder('ascend')}>
            Ascending
          </Button>
          <Button size="sm" variant={sortOrder === 'descend' ? 'primary' : 'outline'} onClick={() => setSortOrder('descend')}>
            Descending
          </Button>
          <Button size="sm" variant={sortOrder === null ? 'primary' : 'outline'} onClick={() => setSortOrder(null)}>
            None
          </Button>
        </Space>
        <Table
          columns={columns}
          dataSource={userData}
          pagination={false}
          onSortChange={(sorter) => setSortOrder(sorter.order ?? null)}
        />
      </div>
    )
}

export default App

Column Filtering

Add filter dropdowns to columns.

Name
Role
Status
John DoeAdminactive
Jane SmithUseractive
Bob JohnsonUserinactive
Alice WilliamsEditoractive
Charlie BrownUseractive
David LeeUseractive
import { Table } from 'asterui'

function App() {
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name' },
    {
      key: 'role',
      title: 'Role',
      dataIndex: 'role',
      filters: [
        { text: 'Admin', value: 'Admin' },
        { text: 'User', value: 'User' },
        { text: 'Editor', value: 'Editor' },
      ],
      onFilter: (value, record) => record.role === value,
    },
    {
      key: 'status',
      title: 'Status',
      dataIndex: 'status',
      filters: [
        { text: 'Active', value: 'active' },
        { text: 'Inactive', value: 'inactive' },
      ],
      onFilter: (value, record) => record.status === value,
    },
  ];
  
  return (
      <Table
        columns={columns}
        dataSource={userData.slice(0, 6)}
        pagination={false}
      />
    )
}

export default App

Checkbox Selection

Enable row selection with checkboxes.

Selected: None
Name
Email
Role
John Doejohn@example.comAdmin
Jane Smithjane@example.comUser
Bob Johnsonbob@example.comUser
Alice Williamsalice@example.comEditor
Charlie Browncharlie@example.comUser
import { Table } from 'asterui'
import { useState } from 'react'

function App() {
  const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
  
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name' },
    { key: 'email', title: 'Email', dataIndex: 'email' },
    { key: 'role', title: 'Role', dataIndex: 'role' },
  ];
  
  const userData = [
    { id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin' },
    { id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
    { id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'User' },
    { id: '4', name: 'Alice Williams', email: 'alice@example.com', role: 'Editor' },
    { id: '5', name: 'Charlie Brown', email: 'charlie@example.com', role: 'User' },
  ];
  
  return (
      <div className="space-y-4">
        <div className="text-sm">Selected: {selectedKeys.join(', ') || 'None'}</div>
        <Table
          columns={columns}
          dataSource={userData}
          rowSelection={{
            selectedRowKeys: selectedKeys,
            onChange: (keys) => setSelectedKeys(keys),
          }}
          pagination={false}
        />
      </div>
    )
}

export default App

Radio Selection

Single row selection with radio buttons.

Selected: None
Name
Email
Role
John Doejohn@example.comAdmin
Jane Smithjane@example.comUser
Bob Johnsonbob@example.comUser
Alice Williamsalice@example.comEditor
Charlie Browncharlie@example.comUser
import { Table } from 'asterui'
import { useState } from 'react'

function App() {
  const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
  
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name' },
    { key: 'email', title: 'Email', dataIndex: 'email' },
    { key: 'role', title: 'Role', dataIndex: 'role' },
  ];
  
  const userData = [
    { id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin' },
    { id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
    { id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'User' },
    { id: '4', name: 'Alice Williams', email: 'alice@example.com', role: 'Editor' },
    { id: '5', name: 'Charlie Brown', email: 'charlie@example.com', role: 'User' },
  ];
  
  return (
      <div className="space-y-4">
        <div className="text-sm">Selected: {selectedKeys.join(', ') || 'None'}</div>
        <Table
          columns={columns}
          dataSource={userData}
          rowSelection={{
            type: 'radio',
            selectedRowKeys: selectedKeys,
            onChange: (keys) => setSelectedKeys(keys),
          }}
          pagination={false}
        />
      </div>
    )
}

export default App

Custom Selection

Selection with custom column title and hidden select-all.

Selected: 0 items
Select
Name
Email
Role
John Doejohn@example.comAdmin
Jane Smithjane@example.comUser
Bob Johnsonbob@example.comUser
Alice Williamsalice@example.comEditor
import { Table } from 'asterui'
import { useState } from 'react'

function App() {
  const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
  
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name' },
    { key: 'email', title: 'Email', dataIndex: 'email' },
    { key: 'role', title: 'Role', dataIndex: 'role' },
  ];
  
  const userData = [
    { id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin' },
    { id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
    { id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'User' },
    { id: '4', name: 'Alice Williams', email: 'alice@example.com', role: 'Editor' },
  ];
  
  return (
      <div className="space-y-4">
        <div className="text-sm">Selected: {selectedKeys.length} items</div>
        <Table
          columns={columns}
          dataSource={userData}
          rowSelection={{
            selectedRowKeys: selectedKeys,
            onChange: (keys) => setSelectedKeys(keys),
            columnTitle: 'Select',
            columnWidth: 80,
            hideSelectAll: true,
          }}
          pagination={false}
        />
      </div>
    )
}

export default App

Expandable Rows

Expand rows to show additional content.

Expand
Name
Email
Role
John Doejohn@example.comAdmin
Jane Smithjane@example.comUser
Bob Johnsonbob@example.comUser
Alice Williamsalice@example.comEditor
Charlie Browncharlie@example.comUser
import { Table } from 'asterui'

function App() {
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name' },
    { key: 'email', title: 'Email', dataIndex: 'email' },
    { key: 'role', title: 'Role', dataIndex: 'role' },
  ];
  
  const userData = [
    { id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'active', age: 32, description: 'Senior administrator' },
    { id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User', status: 'active', age: 28, description: 'Regular user' },
    { id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'User', status: 'inactive', age: 45, description: 'Account suspended' },
    { id: '4', name: 'Alice Williams', email: 'alice@example.com', role: 'Editor', status: 'active', age: 35, description: 'Content editor' },
    { id: '5', name: 'Charlie Brown', email: 'charlie@example.com', role: 'User', status: 'active', age: 29, description: 'New user' },
  ];
  
  const expandable = {
    expandedRowRender: (record) => (
      <div className="p-2">
        <p className="text-sm"><strong>Description:</strong> {record.description}</p>
        <p className="text-sm"><strong>Age:</strong> {record.age}</p>
      </div>
    ),
    rowExpandable: (record) => record.status === 'active',
  };
  
  return (
      <Table
        columns={columns}
        dataSource={userData}
        expandable={expandable}
        pagination={false}
      />
    )
}

export default App

Text Ellipsis

Truncate long text with ellipsis.

Name
Email
Description
John Doejohn@example.comSenior administrator with full system access
Jane Smithjane@example.comRegular user account
Bob Johnsonbob@example.comAccount currently suspended
Alice Williamsalice@example.comContent editor with publishing rights
import { Table } from 'asterui'

function App() {
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name', width: 100 },
    { key: 'email', title: 'Email', dataIndex: 'email', width: 150, ellipsis: true },
    { key: 'description', title: 'Description', dataIndex: 'description', ellipsis: true },
  ];
  
  const userData = [
    { id: '1', name: 'John Doe', email: 'john@example.com', description: 'Senior administrator with full system access' },
    { id: '2', name: 'Jane Smith', email: 'jane@example.com', description: 'Regular user account' },
    { id: '3', name: 'Bob Johnson', email: 'bob@example.com', description: 'Account currently suspended' },
    { id: '4', name: 'Alice Williams', email: 'alice@example.com', description: 'Content editor with publishing rights' },
  ];
  
  return (
      <Table
        columns={columns}
        dataSource={userData}
        pagination={false}
      />
    )
}

export default App

Scroll Configuration

Set max width and height with scrolling.

Name
Email
Role
Status
Age
Description
John Doejohn@example.comAdminactive32Senior administrator
Jane Smithjane@example.comUseractive28Regular user
Bob Johnsonbob@example.comUserinactive45Account suspended
Alice Williamsalice@example.comEditoractive35Content editor
Charlie Browncharlie@example.comUseractive29New user
import { Table } from 'asterui'

function App() {
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name', width: 150 },
    { key: 'email', title: 'Email', dataIndex: 'email', width: 200 },
    { key: 'role', title: 'Role', dataIndex: 'role', width: 100 },
    { key: 'status', title: 'Status', dataIndex: 'status', width: 100 },
    { key: 'age', title: 'Age', dataIndex: 'age', width: 80 },
    { key: 'description', title: 'Description', dataIndex: 'description', width: 300 },
  ];
  
  const userData = [
    { id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'active', age: 32, description: 'Senior administrator' },
    { id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User', status: 'active', age: 28, description: 'Regular user' },
    { id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'User', status: 'inactive', age: 45, description: 'Account suspended' },
    { id: '4', name: 'Alice Williams', email: 'alice@example.com', role: 'Editor', status: 'active', age: 35, description: 'Content editor' },
    { id: '5', name: 'Charlie Brown', email: 'charlie@example.com', role: 'User', status: 'active', age: 29, description: 'New user' },
  ];
  
  return (
      <Table
        columns={columns}
        dataSource={userData}
        scroll={{ x: 800, y: 200 }}
        pagination={false}
      />
    )
}

export default App

Title and Footer

Add title and footer sections to the table.

User List
Name
Email
Role
John Doejohn@example.comAdmin
Jane Smithjane@example.comUser
Bob Johnsonbob@example.comUser
Total: 3 users
import { Table } from 'asterui'

function App() {
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name' },
    { key: 'email', title: 'Email', dataIndex: 'email' },
    { key: 'role', title: 'Role', dataIndex: 'role' },
  ];
  
  const userData = [
    { id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin' },
    { id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
    { id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'User' },
  ];
  
  return (
      <Table
        columns={columns}
        dataSource={userData}
        title={() => <span className="font-semibold">User List</span>}
        footer={(data) => <span>Total: {data.length} users</span>}
        pagination={false}
        bordered
      />
    )
}

export default App

Summary Row

Add a summary row (tfoot) for totals or aggregations.

Product
Quantity
Price
Total
Widget A10$25$250
Widget B5$50$250
Widget C8$30$240
Grand Total:$740
import { Table } from 'asterui'

function App() {
  const columns = [
    { key: 'product', title: 'Product', dataIndex: 'product' },
    { key: 'quantity', title: 'Quantity', dataIndex: 'quantity', align: 'right' },
    { key: 'price', title: 'Price', dataIndex: 'price', align: 'right', render: (v) => `$${v}` },
    { key: 'total', title: 'Total', dataIndex: 'total', align: 'right', render: (v) => `$${v}` },
  ];
  
  const orderData = [
    { id: '1', product: 'Widget A', quantity: 10, price: 25, total: 250 },
    { id: '2', product: 'Widget B', quantity: 5, price: 50, total: 250 },
    { id: '3', product: 'Widget C', quantity: 8, price: 30, total: 240 },
  ];
  
  const grandTotal = orderData.reduce((sum, item) => sum + item.total, 0);
  
  return (
      <Table
        columns={columns}
        dataSource={orderData}
        pagination={false}
        bordered
        summary={() => (
          <tr className="font-semibold bg-base-200">
            <td colSpan={3} className="text-right p-3">Grand Total:</td>
            <td className="text-right p-3">${grandTotal}</td>
          </tr>
        )}
      />
    )
}

export default App

Row Class Name

Apply conditional styling to rows based on data.

Name
Email
Status
John Doejohn@example.comactive
Jane Smithjane@example.comactive
Bob Johnsonbob@example.cominactive
Alice Williamsalice@example.comactive
Charlie Browncharlie@example.cominactive
import { Table } from 'asterui'

function App() {
  const columns = [
    { key: 'name', title: 'Name', dataIndex: 'name' },
    { key: 'email', title: 'Email', dataIndex: 'email' },
    { key: 'status', title: 'Status', dataIndex: 'status' },
  ];
  
  const userData = [
    { id: '1', name: 'John Doe', email: 'john@example.com', status: 'active' },
    { id: '2', name: 'Jane Smith', email: 'jane@example.com', status: 'active' },
    { id: '3', name: 'Bob Johnson', email: 'bob@example.com', status: 'inactive' },
    { id: '4', name: 'Alice Williams', email: 'alice@example.com', status: 'active' },
    { id: '5', name: 'Charlie Brown', email: 'charlie@example.com', status: 'inactive' },
  ];
  
  return (
      <Table
        columns={columns}
        dataSource={userData}
        pagination={false}
        rowClassName={(record) =>
          record.status === 'inactive' ? 'opacity-50 bg-error/10' : ''
        }
      />
    )
}

export default App

Complete Example

Table with multiple features combined.

Name
Email
Role
Status
Actions
John Doejohn@example.comAdminactive
Jane Smithjane@example.comUseractive
Bob Johnsonbob@example.comUserinactive
Alice Williamsalice@example.comEditoractive
Charlie Browncharlie@example.comUseractive
import { Table, Tag, Button, Space } from 'asterui'

function App() {
  return (
      <Table
        columns={columnsWithRender}
        dataSource={userData}
        size="sm"
        striped
        bordered
        pagination={{ pageSize: 5 }}
      />
    )
}

export default App
PropertyDescriptionTypeDefault
columnsTable column configurationColumnType<T>[]-
dataSourceData to display in tableT[]-
rowKeyUnique key for each rowkeyof T | ((record: T) => string)'id'
loadingLoading statebooleanfalse
sizeTable size'xs' | 'sm' | 'md' | 'lg' | 'xl''md'
borderedAdd border around tablebooleanfalse
hoverableHighlight row on hoverbooleantrue
stripedAlternating row colors (zebra stripes)booleanfalse
pinRowsPin header rows while scrollingbooleanfalse
pinColsPin columns while scrollingbooleanfalse
paginationPagination configuration or false to disablefalse | PaginationConfig{ pageSize: 10 }
rowSelectionRow selection configurationRowSelection<T>-
expandableExpandable row configurationExpandableConfig<T>-
scrollScroll configuration for fixed dimensionsScrollConfig-
classNameAdditional CSS classesstring-
showHeaderWhether to show the table headerbooleantrue
rowClassNameCustom class name for each rowstring | ((record: T, index: number) => string)-
titleTable title renderer(currentPageData: T[]) => ReactNode-
footerTable footer renderer(currentPageData: T[]) => ReactNode-
summaryTable summary (tfoot) renderer(currentPageData: T[]) => ReactNode-
onRowRow event handlers(record: T, index: number) => HTMLAttributes-
onHeaderRowHeader row event handlers(columns: ColumnType<T>[]) => HTMLAttributes-
onChangeUnified callback for pagination, sorting, and filtering(pagination, filters, sorter, extra) => void-
onSortChangeCallback when sort changes(sorter: SorterResult<T>) => void-
onFilterChangeCallback when filter changes(filters: Record<string, ...>) => void-
localeLocalization config for empty text, filter labels{ emptyText?, filterConfirm?, filterReset?, selectAll? }-
data-testidTest ID for the componentstring'table'
aria-labelAccessible label for the tablestring-
PropertyDescriptionTypeDefault
keyUnique key for columnstring-
titleColumn header titleReactNode-
dataIndexField name in data recordkeyof T-
renderCustom render function for cell content(value, record, index) => ReactNode-
widthColumn widthstring | number-
alignText alignment'left' | 'center' | 'right''left'
fixedFix column to left or right'left' | 'right'-
sorterEnable sorting or custom sort functionboolean | ((a: T, b: T) => number)-
sortOrderControlled sort order'ascend' | 'descend' | null-
defaultSortOrderDefault sort order'ascend' | 'descend'-
filtersFilter dropdown configurationFilterConfig[]-
filteredValueControlled filtered values(string | number | boolean)[]-
defaultFilteredValueDefault filtered values(string | number | boolean)[]-
onFilterFilter function(value, record) => boolean-
ellipsisTruncate text with ellipsisbooleanfalse
hiddenHide columnbooleanfalse
classNameCustom class name for column cellsstring-
onCellCustom cell attributes(record: T, index: number) => TdHTMLAttributes-
onHeaderCellCustom header cell attributes(column: ColumnType<T>) => ThHTMLAttributes-
PropertyDescriptionTypeDefault
typeSelection type'checkbox' | 'radio''checkbox'
selectedRowKeysControlled selected row keysReact.Key[]-
onChangeCallback when selection changes(selectedRowKeys, selectedRows) => void-
onSelectCallback when a row is selected(record, selected, selectedRows, nativeEvent) => void-
getCheckboxPropsFunction to customize checkbox props(record) => { disabled?, name? }-
columnTitleCustom title for selection columnReactNode-
columnWidthWidth of selection columnstring | number50
fixedFix selection column to leftbooleantrue
hideSelectAllHide select all checkboxbooleanfalse
PropertyDescriptionTypeDefault
expandedRowRenderRender function for expanded content(record, index, expanded) => ReactNode-
expandedRowKeysControlled expanded row keysReact.Key[]-
defaultExpandedRowKeysDefault expanded row keysReact.Key[]-
defaultExpandAllRowsExpand all rows by defaultbooleanfalse
rowExpandableFunction to determine if row is expandable(record) => boolean-
onExpandCallback when expand state changes(expanded, record) => void-
onExpandedRowsChangeCallback when expanded keys change(expandedKeys) => void-
expandRowByClickExpand row on row clickbooleanfalse
expandIconCustom expand icon(props) => ReactNode-
columnTitleCustom title for expand columnReactNode-
columnWidthWidth of expand columnstring | number50
expandedRowClassNameCustom class for expanded rowstring | ((record, index, expanded) => string)-
showExpandColumnWhether to show expand columnbooleantrue
PropertyDescriptionTypeDefault
currentCurrent page numbernumber1
pageSizeNumber of items per pagenumber10
totalTotal number of items (for server-side pagination)number-
showSizeChangerShow page size dropdownbooleanfalse
showQuickJumperShow quick page jumper inputbooleanfalse
showTotalFunction to render total text(total, range) => ReactNode-
pageSizeOptionsPage size optionsnumber[][10, 20, 50, 100]
positionPagination position'topLeft' | 'topCenter' | 'topRight' | 'bottomLeft' | 'bottomCenter' | 'bottomRight''bottomRight'
onChangeCallback when page changes(page, pageSize) => void-
onShowSizeChangeCallback when page size changes(current, size) => void-
  • Uses semantic <table> with role="grid"
  • Column headers include role="columnheader" and aria-sort for sortable columns
  • Row selection checkboxes have proper aria-label
  • Sort controls are keyboard accessible (Enter/Space to toggle)
  • Filter dropdowns support Escape key to close
  • Pagination buttons include aria-label and aria-current
  • Expand/collapse buttons include aria-expanded
  • Indeterminate checkbox state for partial selection

The component exposes data-testid attributes:

  • {baseTestId} - Root wrapper
  • {baseTestId}-table - Table element
  • {baseTestId}-title - Table title
  • {baseTestId}-footer - Table footer
  • {baseTestId}-summary - Table summary (tfoot)
  • {baseTestId}-header-{columnKey} - Column headers
  • {baseTestId}-row-{index} - Table rows
  • {baseTestId}-row-{index}-{columnKey} - Table cells
  • {baseTestId}-row-{index}-select - Row selection checkbox
  • {baseTestId}-row-{index}-expanded - Expanded row content
  • {baseTestId}-select-all - Select all checkbox
  • {baseTestId}-pagination - Pagination container
  • {baseTestId}-prev, {baseTestId}-next - Pagination buttons
  • {baseTestId}-{columnKey}-filter-button - Filter button
  • {baseTestId}-empty - Empty state
  • {baseTestId}-loading - Loading state

Data attributes:

  • data-state="selected" - On selected rows