Table
Feature-rich table component for displaying tabular data.
Import
Section titled “Import”import { Table } from 'asterui'import type { ColumnType, RowSelection, ExpandableConfig, PaginationConfig } from 'asterui'Examples
Section titled “Examples”Basic Table
Simple table with data.
Name | Email | Role |
|---|---|---|
| John Doe | john@example.com | Admin |
| Jane Smith | jane@example.com | User |
| Bob Johnson | bob@example.com | User |
| Alice Williams | alice@example.com | Editor |
| Charlie Brown | charlie@example.com | User |
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 Doe | john@example.com | Admin | active | |
| Jane Smith | jane@example.com | User | active | |
| Bob Johnson | bob@example.com | User | inactive | |
| Alice Williams | alice@example.com | Editor | active |
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 Doe | john@example.com | Admin |
| Jane Smith | jane@example.com | User |
| Bob Johnson | bob@example.com | User |
| Alice Williams | alice@example.com | Editor |
| Charlie Brown | charlie@example.com | User |
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 Doe | john@example.com | Admin |
| Jane Smith | jane@example.com | User |
| Bob Johnson | bob@example.com | User |
| Alice Williams | alice@example.com | Editor |
| Charlie Brown | charlie@example.com | User |
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 Doe | john@example.com | Admin |
| Jane Smith | jane@example.com | User |
| Bob Johnson | bob@example.com | User |
| Alice Williams | alice@example.com | Editor |
| Charlie Brown | charlie@example.com | User |
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 Doe | john@example.com | Admin |
| Jane Smith | jane@example.com | User |
| Bob Johnson | bob@example.com | User |
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 Doe | john@example.com | Admin |
| Jane Smith | jane@example.com | User |
| Bob Johnson | bob@example.com | User |
| Alice Williams | alice@example.com | Editor |
| Charlie Brown | charlie@example.com | User |
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 Doe | john@example.com | Admin |
| Jane Smith | jane@example.com | User |
| Bob Johnson | bob@example.com | User |
| Alice Williams | alice@example.com | Editor |
| Charlie Brown | charlie@example.com | User |
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 Doe | john@example.com | Admin |
| Jane Smith | jane@example.com | User |
| Bob Johnson | bob@example.com | User |
| Alice Williams | alice@example.com | Editor |
| Charlie Brown | charlie@example.com | User |
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 Doe | john@example.com | Admin |
| Jane Smith | jane@example.com | User |
| Bob Johnson | bob@example.com | User |
| Alice Williams | alice@example.com | Editor |
| Charlie Brown | charlie@example.com | User |
| David Lee | david@example.com | User |
| Emma Wilson | emma@example.com | Editor |
| Frank Miller | frank@example.com | User |
| Grace Taylor | grace@example.com | Admin |
| Henry Davis | henry@example.com | User |
| Iris Martin | iris@example.com | Editor |
| Jack White | jack@example.com | User |
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 Smith | 28 | jane@example.com |
| Charlie Brown | 29 | charlie@example.com |
| John Doe | 32 | john@example.com |
| Alice Williams | 35 | alice@example.com |
| David Lee | 41 | david@example.com |
| Bob Johnson | 45 | bob@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 Williams | alice@example.com | Editor |
| Bob Johnson | bob@example.com | User |
| Charlie Brown | charlie@example.com | User |
| Jane Smith | jane@example.com | User |
| John Doe | john@example.com | Admin |
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 Doe | Admin | active |
| Jane Smith | User | active |
| Bob Johnson | User | inactive |
| Alice Williams | Editor | active |
| Charlie Brown | User | active |
| David Lee | User | active |
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.
Name | Email | Role | |
|---|---|---|---|
| John Doe | john@example.com | Admin | |
| Jane Smith | jane@example.com | User | |
| Bob Johnson | bob@example.com | User | |
| Alice Williams | alice@example.com | Editor | |
| Charlie Brown | charlie@example.com | User |
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.
Name | Email | Role | |
|---|---|---|---|
| John Doe | john@example.com | Admin | |
| Jane Smith | jane@example.com | User | |
| Bob Johnson | bob@example.com | User | |
| Alice Williams | alice@example.com | Editor | |
| Charlie Brown | charlie@example.com | User |
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.
| Select | Name | Email | Role |
|---|---|---|---|
| John Doe | john@example.com | Admin | |
| Jane Smith | jane@example.com | User | |
| Bob Johnson | bob@example.com | User | |
| Alice Williams | alice@example.com | Editor |
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 Doe | john@example.com | Admin | |
| Jane Smith | jane@example.com | User | |
| Bob Johnson | bob@example.com | User | |
| Alice Williams | alice@example.com | Editor | |
| Charlie Brown | charlie@example.com | User |
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 Doe | john@example.com | Senior administrator with full system access |
| Jane Smith | jane@example.com | Regular user account |
| Bob Johnson | bob@example.com | Account currently suspended |
| Alice Williams | alice@example.com | Content 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 Doe | john@example.com | Admin | active | 32 | Senior administrator |
| Jane Smith | jane@example.com | User | active | 28 | Regular user |
| Bob Johnson | bob@example.com | User | inactive | 45 | Account suspended |
| Alice Williams | alice@example.com | Editor | active | 35 | Content editor |
| Charlie Brown | charlie@example.com | User | active | 29 | New 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.
Name | Email | Role |
|---|---|---|
| John Doe | john@example.com | Admin |
| Jane Smith | jane@example.com | User |
| Bob Johnson | bob@example.com | User |
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 A | 10 | $25 | $250 |
| Widget B | 5 | $50 | $250 |
| Widget C | 8 | $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 Doe | john@example.com | active |
| Jane Smith | jane@example.com | active |
| Bob Johnson | bob@example.com | inactive |
| Alice Williams | alice@example.com | active |
| Charlie Brown | charlie@example.com | inactive |
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 Doe | john@example.com | Admin | active | |
| Jane Smith | jane@example.com | User | active | |
| Bob Johnson | bob@example.com | User | inactive | |
| Alice Williams | alice@example.com | Editor | active | |
| Charlie Brown | charlie@example.com | User | active |
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 | Property | Description | Type | Default |
|---|---|---|---|
columns | Table column configuration | ColumnType<T>[] | - |
dataSource | Data to display in table | T[] | - |
rowKey | Unique key for each row | keyof T | ((record: T) => string) | 'id' |
loading | Loading state | boolean | false |
size | Table size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' |
bordered | Add border around table | boolean | false |
hoverable | Highlight row on hover | boolean | true |
striped | Alternating row colors (zebra stripes) | boolean | false |
pinRows | Pin header rows while scrolling | boolean | false |
pinCols | Pin columns while scrolling | boolean | false |
pagination | Pagination configuration or false to disable | false | PaginationConfig | { pageSize: 10 } |
rowSelection | Row selection configuration | RowSelection<T> | - |
expandable | Expandable row configuration | ExpandableConfig<T> | - |
scroll | Scroll configuration for fixed dimensions | ScrollConfig | - |
className | Additional CSS classes | string | - |
showHeader | Whether to show the table header | boolean | true |
rowClassName | Custom class name for each row | string | ((record: T, index: number) => string) | - |
title | Table title renderer | (currentPageData: T[]) => ReactNode | - |
footer | Table footer renderer | (currentPageData: T[]) => ReactNode | - |
summary | Table summary (tfoot) renderer | (currentPageData: T[]) => ReactNode | - |
onRow | Row event handlers | (record: T, index: number) => HTMLAttributes | - |
onHeaderRow | Header row event handlers | (columns: ColumnType<T>[]) => HTMLAttributes | - |
onChange | Unified callback for pagination, sorting, and filtering | (pagination, filters, sorter, extra) => void | - |
onSortChange | Callback when sort changes | (sorter: SorterResult<T>) => void | - |
onFilterChange | Callback when filter changes | (filters: Record<string, ...>) => void | - |
locale | Localization config for empty text, filter labels | { emptyText?, filterConfirm?, filterReset?, selectAll? } | - |
data-testid | Test ID for the component | string | 'table' |
aria-label | Accessible label for the table | string | - |
ColumnType
Section titled “ColumnType”| Property | Description | Type | Default |
|---|---|---|---|
key | Unique key for column | string | - |
title | Column header title | ReactNode | - |
dataIndex | Field name in data record | keyof T | - |
render | Custom render function for cell content | (value, record, index) => ReactNode | - |
width | Column width | string | number | - |
align | Text alignment | 'left' | 'center' | 'right' | 'left' |
fixed | Fix column to left or right | 'left' | 'right' | - |
sorter | Enable sorting or custom sort function | boolean | ((a: T, b: T) => number) | - |
sortOrder | Controlled sort order | 'ascend' | 'descend' | null | - |
defaultSortOrder | Default sort order | 'ascend' | 'descend' | - |
filters | Filter dropdown configuration | FilterConfig[] | - |
filteredValue | Controlled filtered values | (string | number | boolean)[] | - |
defaultFilteredValue | Default filtered values | (string | number | boolean)[] | - |
onFilter | Filter function | (value, record) => boolean | - |
ellipsis | Truncate text with ellipsis | boolean | false |
hidden | Hide column | boolean | false |
className | Custom class name for column cells | string | - |
onCell | Custom cell attributes | (record: T, index: number) => TdHTMLAttributes | - |
onHeaderCell | Custom header cell attributes | (column: ColumnType<T>) => ThHTMLAttributes | - |
RowSelection
Section titled “RowSelection”| Property | Description | Type | Default |
|---|---|---|---|
type | Selection type | 'checkbox' | 'radio' | 'checkbox' |
selectedRowKeys | Controlled selected row keys | React.Key[] | - |
onChange | Callback when selection changes | (selectedRowKeys, selectedRows) => void | - |
onSelect | Callback when a row is selected | (record, selected, selectedRows, nativeEvent) => void | - |
getCheckboxProps | Function to customize checkbox props | (record) => { disabled?, name? } | - |
columnTitle | Custom title for selection column | ReactNode | - |
columnWidth | Width of selection column | string | number | 50 |
fixed | Fix selection column to left | boolean | true |
hideSelectAll | Hide select all checkbox | boolean | false |
ExpandableConfig
Section titled “ExpandableConfig”| Property | Description | Type | Default |
|---|---|---|---|
expandedRowRender | Render function for expanded content | (record, index, expanded) => ReactNode | - |
expandedRowKeys | Controlled expanded row keys | React.Key[] | - |
defaultExpandedRowKeys | Default expanded row keys | React.Key[] | - |
defaultExpandAllRows | Expand all rows by default | boolean | false |
rowExpandable | Function to determine if row is expandable | (record) => boolean | - |
onExpand | Callback when expand state changes | (expanded, record) => void | - |
onExpandedRowsChange | Callback when expanded keys change | (expandedKeys) => void | - |
expandRowByClick | Expand row on row click | boolean | false |
expandIcon | Custom expand icon | (props) => ReactNode | - |
columnTitle | Custom title for expand column | ReactNode | - |
columnWidth | Width of expand column | string | number | 50 |
expandedRowClassName | Custom class for expanded row | string | ((record, index, expanded) => string) | - |
showExpandColumn | Whether to show expand column | boolean | true |
PaginationConfig
Section titled “PaginationConfig”| Property | Description | Type | Default |
|---|---|---|---|
current | Current page number | number | 1 |
pageSize | Number of items per page | number | 10 |
total | Total number of items (for server-side pagination) | number | - |
showSizeChanger | Show page size dropdown | boolean | false |
showQuickJumper | Show quick page jumper input | boolean | false |
showTotal | Function to render total text | (total, range) => ReactNode | - |
pageSizeOptions | Page size options | number[] | [10, 20, 50, 100] |
position | Pagination position | 'topLeft' | 'topCenter' | 'topRight' | 'bottomLeft' | 'bottomCenter' | 'bottomRight' | 'bottomRight' |
onChange | Callback when page changes | (page, pageSize) => void | - |
onShowSizeChange | Callback when page size changes | (current, size) => void | - |
Accessibility
Section titled “Accessibility”- Uses semantic
<table>withrole="grid" - Column headers include
role="columnheader"andaria-sortfor 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-labelandaria-current - Expand/collapse buttons include
aria-expanded - Indeterminate checkbox state for partial selection
Testing
Section titled “Testing”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