- Basic Usage
- JSX style API
- selection
- Selection and operation
- Custom selection
- Filter and sorter
- Filter in Tree
- Multiple sorter
- Reset filters and sorters
- Customized filter panel
- Ajax
- size
- border, title and footer
- Expandable Row
- Order Specific Column
- colSpan and rowSpan
- Tree data
- Fixed Header
- Fixed Columns
- Fixed Columns and Header
- Grouping table head
- Editable Cells
- Editable Rows
- Nested tables
- Drag sorting
- Drag sorting with handler
- ellipsis column
- ellipsis column custom tooltip
- Summary
- Virtual list
- Responsive
- Pagination Settings
- Fixed header and scroll bar with the page
- Dynamic Settings
- API
Table
A table displays rows of data.
When To Use#
To display a collection of structured data.
To sort, search, paginate, filter data.
How To Use#
Specify dataSource
of Table as an array of data.
const dataSource = [
{
key: '1',
name: 'Mike',
age: 32,
address: '10 Downing Street',
},
{
key: '2',
name: 'John',
age: 42,
address: '10 Downing Street',
},
];
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
];
<Table dataSource={dataSource} columns={columns} />;
Examples
Name | Age | Address | Tags | Action |
---|---|---|---|---|
John Brown | 32 | New York No. 1 Lake Park | NICEDEVELOPER | |
Jim Green | 42 | London No. 1 Lake Park | LOSER | |
Joe Black | 32 | Sidney No. 1 Lake Park | COOLTEACHER |
import { Table, Tag, Space } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: text => <a>{text}</a>,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
{
title: 'Tags',
key: 'tags',
dataIndex: 'tags',
render: tags => (
<>
{tags.map(tag => {
let color = tag.length > 5 ? 'geekblue' : 'green';
if (tag === 'loser') {
color = 'volcano';
}
return (
<Tag color={color} key={tag}>
{tag.toUpperCase()}
</Tag>
);
})}
</>
),
},
{
title: 'Action',
key: 'action',
render: (text, record) => (
<Space size="middle">
<a>Invite {record.name}</a>
<a>Delete</a>
</Space>
),
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
ReactDOM.render(<Table columns={columns} dataSource={data} />, mountNode);
Name | Age | Address | Tags | Action | |
---|---|---|---|---|---|
First Name | Last Name | ||||
John | Brown | 32 | New York No. 1 Lake Park | nicedeveloper | |
Jim | Green | 42 | London No. 1 Lake Park | loser | |
Joe | Black | 32 | Sidney No. 1 Lake Park | coolteacher |
import { Table, Tag, Space } from 'antd';
const { Column, ColumnGroup } = Table;
const data = [
{
key: '1',
firstName: 'John',
lastName: 'Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
firstName: 'Jim',
lastName: 'Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
},
{
key: '3',
firstName: 'Joe',
lastName: 'Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
ReactDOM.render(
<Table dataSource={data}>
<ColumnGroup title="Name">
<Column title="First Name" dataIndex="firstName" key="firstName" />
<Column title="Last Name" dataIndex="lastName" key="lastName" />
</ColumnGroup>
<Column title="Age" dataIndex="age" key="age" />
<Column title="Address" dataIndex="address" key="address" />
<Column
title="Tags"
dataIndex="tags"
key="tags"
render={tags => (
<>
{tags.map(tag => (
<Tag color="blue" key={tag}>
{tag}
</Tag>
))}
</>
)}
/>
<Column
title="Action"
key="action"
render={(text, record) => (
<Space size="middle">
<a>Invite {record.lastName}</a>
<a>Delete</a>
</Space>
)}
/>
</Table>,
mountNode,
);
Name | Age | Address | |
---|---|---|---|
John Brown | 32 | New York No. 1 Lake Park | |
Jim Green | 42 | London No. 1 Lake Park | |
Joe Black | 32 | Sidney No. 1 Lake Park | |
Disabled User | 99 | Sidney No. 1 Lake Park |
import React, { useState } from 'react';
import { Table, Radio, Divider } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
render: (text: string) => <a>{text}</a>,
},
{
title: 'Age',
dataIndex: 'age',
},
{
title: 'Address',
dataIndex: 'address',
},
];
interface DataType {
key: React.Key;
name: string;
age: number;
address: string;
}
const data: DataType[] = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Disabled User',
age: 99,
address: 'Sidney No. 1 Lake Park',
},
];
// rowSelection object indicates the need for row selection
const rowSelection = {
onChange: (selectedRowKeys: React.Key[], selectedRows: DataType[]) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
getCheckboxProps: (record: DataType) => ({
disabled: record.name === 'Disabled User', // Column configuration not to be checked
name: record.name,
}),
};
const Demo = () => {
const [selectionType, setSelectionType] = useState<'checkbox' | 'radio'>('checkbox');
return (
<div>
<Radio.Group
onChange={({ target: { value } }) => {
setSelectionType(value);
}}
value={selectionType}
>
<Radio value="checkbox">Checkbox</Radio>
<Radio value="radio">radio</Radio>
</Radio.Group>
<Divider />
<Table
rowSelection={{
type: selectionType,
...rowSelection,
}}
columns={columns}
dataSource={data}
/>
</div>
);
};
ReactDOM.render(<Demo />, mountNode);
Name | Age | Address | |
---|---|---|---|
Edward King 0 | 32 | London, Park Lane no. 0 | |
Edward King 1 | 32 | London, Park Lane no. 1 | |
Edward King 2 | 32 | London, Park Lane no. 2 | |
Edward King 3 | 32 | London, Park Lane no. 3 | |
Edward King 4 | 32 | London, Park Lane no. 4 | |
Edward King 5 | 32 | London, Park Lane no. 5 | |
Edward King 6 | 32 | London, Park Lane no. 6 | |
Edward King 7 | 32 | London, Park Lane no. 7 | |
Edward King 8 | 32 | London, Park Lane no. 8 | |
Edward King 9 | 32 | London, Park Lane no. 9 |
import { Table, Button } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Age',
dataIndex: 'age',
},
{
title: 'Address',
dataIndex: 'address',
},
];
const data = [];
for (let i = 0; i < 46; i++) {
data.push({
key: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`,
});
}
class App extends React.Component {
state = {
selectedRowKeys: [], // Check here to configure the default column
loading: false,
};
start = () => {
this.setState({ loading: true });
// ajax request after empty completing
setTimeout(() => {
this.setState({
selectedRowKeys: [],
loading: false,
});
}, 1000);
};
onSelectChange = selectedRowKeys => {
console.log('selectedRowKeys changed: ', selectedRowKeys);
this.setState({ selectedRowKeys });
};
render() {
const { loading, selectedRowKeys } = this.state;
const rowSelection = {
selectedRowKeys,
onChange: this.onSelectChange,
};
const hasSelected = selectedRowKeys.length > 0;
return (
<div>
<div style={{ marginBottom: 16 }}>
<Button type="primary" onClick={this.start} disabled={!hasSelected} loading={loading}>
Reload
</Button>
<span style={{ marginLeft: 8 }}>
{hasSelected ? `Selected ${selectedRowKeys.length} items` : ''}
</span>
</div>
<Table rowSelection={rowSelection} columns={columns} dataSource={data} />
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
Name | Age | Address | |
---|---|---|---|
Edward King 0 | 32 | London, Park Lane no. 0 | |
Edward King 1 | 32 | London, Park Lane no. 1 | |
Edward King 2 | 32 | London, Park Lane no. 2 | |
Edward King 3 | 32 | London, Park Lane no. 3 | |
Edward King 4 | 32 | London, Park Lane no. 4 | |
Edward King 5 | 32 | London, Park Lane no. 5 | |
Edward King 6 | 32 | London, Park Lane no. 6 | |
Edward King 7 | 32 | London, Park Lane no. 7 | |
Edward King 8 | 32 | London, Park Lane no. 8 | |
Edward King 9 | 32 | London, Park Lane no. 9 |
import { Table } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Age',
dataIndex: 'age',
},
{
title: 'Address',
dataIndex: 'address',
},
];
const data = [];
for (let i = 0; i < 46; i++) {
data.push({
key: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`,
});
}
class App extends React.Component {
state = {
selectedRowKeys: [], // Check here to configure the default column
};
onSelectChange = selectedRowKeys => {
console.log('selectedRowKeys changed: ', selectedRowKeys);
this.setState({ selectedRowKeys });
};
render() {
const { selectedRowKeys } = this.state;
const rowSelection = {
selectedRowKeys,
onChange: this.onSelectChange,
selections: [
Table.SELECTION_ALL,
Table.SELECTION_INVERT,
Table.SELECTION_NONE,
{
key: 'odd',
text: 'Select Odd Row',
onSelect: changableRowKeys => {
let newSelectedRowKeys = [];
newSelectedRowKeys = changableRowKeys.filter((key, index) => {
if (index % 2 !== 0) {
return false;
}
return true;
});
this.setState({ selectedRowKeys: newSelectedRowKeys });
},
},
{
key: 'even',
text: 'Select Even Row',
onSelect: changableRowKeys => {
let newSelectedRowKeys = [];
newSelectedRowKeys = changableRowKeys.filter((key, index) => {
if (index % 2 !== 0) {
return true;
}
return false;
});
this.setState({ selectedRowKeys: newSelectedRowKeys });
},
},
],
};
return <Table rowSelection={rowSelection} columns={columns} dataSource={data} />;
}
}
ReactDOM.render(<App />, mountNode);
Name | Age | Address |
---|---|---|
Jim Green | 42 | London No. 1 Lake Park |
John Brown | 32 | New York No. 1 Lake Park |
Joe Black | 32 | Sidney No. 1 Lake Park |
Jim Red | 32 | London No. 2 Lake Park |
import { Table } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
filters: [
{
text: 'Joe',
value: 'Joe',
},
{
text: 'Jim',
value: 'Jim',
},
{
text: 'Submenu',
value: 'Submenu',
children: [
{
text: 'Green',
value: 'Green',
},
{
text: 'Black',
value: 'Black',
},
],
},
],
// specify the condition of filtering result
// here is that finding the name started with `value`
onFilter: (value, record) => record.name.indexOf(value) === 0,
sorter: (a, b) => a.name.length - b.name.length,
sortDirections: ['descend'],
},
{
title: 'Age',
dataIndex: 'age',
defaultSortOrder: 'descend',
sorter: (a, b) => a.age - b.age,
},
{
title: 'Address',
dataIndex: 'address',
filters: [
{
text: 'London',
value: 'London',
},
{
text: 'New York',
value: 'New York',
},
],
onFilter: (value, record) => record.address.indexOf(value) === 0,
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
},
];
function onChange(pagination, filters, sorter, extra) {
console.log('params', pagination, filters, sorter, extra);
}
ReactDOM.render(<Table columns={columns} dataSource={data} onChange={onChange} />, mountNode);
Name | Age | Address |
---|---|---|
John Brown | 32 | New York No. 1 Lake Park |
Jim Green | 42 | London No. 1 Lake Park |
Joe Black | 32 | Sidney No. 1 Lake Park |
Jim Red | 32 | London No. 2 Lake Park |
import { Table } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
filters: [
{
text: 'Joe',
value: 'Joe',
},
{
text: 'Category 1',
value: 'Category 1',
children: [
{
text: 'Yellow',
value: 'Yellow',
},
{
text: 'Pink',
value: 'Pink',
},
],
},
{
text: 'Category 2',
value: 'Category 2',
children: [
{
text: 'Green',
value: 'Green',
},
{
text: 'Black',
value: 'Black',
},
],
},
],
filterMode: 'tree',
filterSearch: true,
onFilter: (value, record) => record.name.includes(value),
width: '30%',
},
{
title: 'Age',
dataIndex: 'age',
sorter: (a, b) => a.age - b.age,
},
{
title: 'Address',
dataIndex: 'address',
filters: [
{
text: 'London',
value: 'London',
},
{
text: 'New York',
value: 'New York',
},
],
onFilter: (value, record) => record.address.startsWith(value),
filterSearch: true,
width: '40%',
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
},
];
function onChange(pagination, filters, sorter, extra) {
console.log('params', pagination, filters, sorter, extra);
}
ReactDOM.render(<Table columns={columns} dataSource={data} onChange={onChange} />, mountNode);
Name | Chinese Score | Math Score | English Score |
---|---|---|---|
John Brown | 98 | 60 | 70 |
Jim Green | 98 | 66 | 89 |
Joe Black | 98 | 90 | 70 |
Jim Red | 88 | 99 | 89 |
import { Table } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Chinese Score',
dataIndex: 'chinese',
sorter: {
compare: (a, b) => a.chinese - b.chinese,
multiple: 3,
},
},
{
title: 'Math Score',
dataIndex: 'math',
sorter: {
compare: (a, b) => a.math - b.math,
multiple: 2,
},
},
{
title: 'English Score',
dataIndex: 'english',
sorter: {
compare: (a, b) => a.english - b.english,
multiple: 1,
},
},
];
const data = [
{
key: '1',
name: 'John Brown',
chinese: 98,
math: 60,
english: 70,
},
{
key: '2',
name: 'Jim Green',
chinese: 98,
math: 66,
english: 89,
},
{
key: '3',
name: 'Joe Black',
chinese: 98,
math: 90,
english: 70,
},
{
key: '4',
name: 'Jim Red',
chinese: 88,
math: 99,
english: 89,
},
];
function onChange(pagination, filters, sorter, extra) {
console.log('params', pagination, filters, sorter, extra);
}
ReactDOM.render(<Table columns={columns} dataSource={data} onChange={onChange} />, mountNode);
Name | Age | Address |
---|---|---|
John Brown | 32 | New York No. 1 Lake Park |
Jim Green | 42 | London No. 1 Lake Park |
Joe Black | 32 | Sidney No. 1 Lake Park |
Jim Red | 32 | London No. 2 Lake Park |
import { Table, Button, Space } from 'antd';
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
},
];
class App extends React.Component {
state = {
filteredInfo: null,
sortedInfo: null,
};
handleChange = (pagination, filters, sorter) => {
console.log('Various parameters', pagination, filters, sorter);
this.setState({
filteredInfo: filters,
sortedInfo: sorter,
});
};
clearFilters = () => {
this.setState({ filteredInfo: null });
};
clearAll = () => {
this.setState({
filteredInfo: null,
sortedInfo: null,
});
};
setAgeSort = () => {
this.setState({
sortedInfo: {
order: 'descend',
columnKey: 'age',
},
});
};
render() {
let { sortedInfo, filteredInfo } = this.state;
sortedInfo = sortedInfo || {};
filteredInfo = filteredInfo || {};
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
filters: [
{ text: 'Joe', value: 'Joe' },
{ text: 'Jim', value: 'Jim' },
],
filteredValue: filteredInfo.name || null,
onFilter: (value, record) => record.name.includes(value),
sorter: (a, b) => a.name.length - b.name.length,
sortOrder: sortedInfo.columnKey === 'name' && sortedInfo.order,
ellipsis: true,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
sorter: (a, b) => a.age - b.age,
sortOrder: sortedInfo.columnKey === 'age' && sortedInfo.order,
ellipsis: true,
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
filters: [
{ text: 'London', value: 'London' },
{ text: 'New York', value: 'New York' },
],
filteredValue: filteredInfo.address || null,
onFilter: (value, record) => record.address.includes(value),
sorter: (a, b) => a.address.length - b.address.length,
sortOrder: sortedInfo.columnKey === 'address' && sortedInfo.order,
ellipsis: true,
},
];
return (
<>
<Space style={{ marginBottom: 16 }}>
<Button onClick={this.setAgeSort}>Sort age</Button>
<Button onClick={this.clearFilters}>Clear filters</Button>
<Button onClick={this.clearAll}>Clear filters and sorters</Button>
</Space>
<Table columns={columns} dataSource={data} onChange={this.handleChange} />
</>
);
}
}
ReactDOM.render(<App />, mountNode);
Name | Age | Address |
---|---|---|
John Brown | 32 | New York No. 1 Lake Park |
Joe Black | 42 | London No. 1 Lake Park |
Jim Green | 32 | Sidney No. 1 Lake Park |
Jim Red | 32 | London No. 2 Lake Park |
import { Table, Input, Button, Space } from 'antd';
import Highlighter from 'react-highlight-words';
import { SearchOutlined } from '@ant-design/icons';
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Joe Black',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Jim Green',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
},
];
class App extends React.Component {
state = {
searchText: '',
searchedColumn: '',
};
getColumnSearchProps = dataIndex => ({
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
ref={node => {
this.searchInput = node;
}}
placeholder={`Search ${dataIndex}`}
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => this.handleSearch(selectedKeys, confirm, dataIndex)}
style={{ marginBottom: 8, display: 'block' }}
/>
<Space>
<Button
type="primary"
onClick={() => this.handleSearch(selectedKeys, confirm, dataIndex)}
icon={<SearchOutlined />}
size="small"
style={{ width: 90 }}
>
Search
</Button>
<Button onClick={() => this.handleReset(clearFilters)} size="small" style={{ width: 90 }}>
Reset
</Button>
<Button
type="link"
size="small"
onClick={() => {
confirm({ closeDropdown: false });
this.setState({
searchText: selectedKeys[0],
searchedColumn: dataIndex,
});
}}
>
Filter
</Button>
</Space>
</div>
),
filterIcon: filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />,
onFilter: (value, record) =>
record[dataIndex]
? record[dataIndex].toString().toLowerCase().includes(value.toLowerCase())
: '',
onFilterDropdownVisibleChange: visible => {
if (visible) {
setTimeout(() => this.searchInput.select(), 100);
}
},
render: text =>
this.state.searchedColumn === dataIndex ? (
<Highlighter
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
searchWords={[this.state.searchText]}
autoEscape
textToHighlight={text ? text.toString() : ''}
/>
) : (
text
),
});
handleSearch = (selectedKeys, confirm, dataIndex) => {
confirm();
this.setState({
searchText: selectedKeys[0],
searchedColumn: dataIndex,
});
};
handleReset = clearFilters => {
clearFilters();
this.setState({ searchText: '' });
};
render() {
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
width: '30%',
...this.getColumnSearchProps('name'),
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: '20%',
...this.getColumnSearchProps('age'),
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
...this.getColumnSearchProps('address'),
sorter: (a, b) => a.address.length - b.address.length,
sortDirections: ['descend', 'ascend'],
},
];
return <Table columns={columns} dataSource={data} />;
}
}
ReactDOM.render(<App />, mountNode);
Name | Gender | |
---|---|---|
No Data |
import { Table } from 'antd';
import qs from 'qs';
const columns = [
{
title: 'Name',
dataIndex: 'name',
sorter: true,
render: name => `${name.first} ${name.last}`,
width: '20%',
},
{
title: 'Gender',
dataIndex: 'gender',
filters: [
{ text: 'Male', value: 'male' },
{ text: 'Female', value: 'female' },
],
width: '20%',
},
{
title: 'Email',
dataIndex: 'email',
},
];
const getRandomuserParams = params => ({
results: params.pagination.pageSize,
page: params.pagination.current,
...params,
});
class App extends React.Component {
state = {
data: [],
pagination: {
current: 1,
pageSize: 10,
},
loading: false,
};
componentDidMount() {
const { pagination } = this.state;
this.fetch({ pagination });
}
handleTableChange = (pagination, filters, sorter) => {
this.fetch({
sortField: sorter.field,
sortOrder: sorter.order,
pagination,
...filters,
});
};
fetch = (params = {}) => {
this.setState({ loading: true });
fetch(`https://randomuser.me/api?${qs.stringify(getRandomuserParams(params))}`)
.then(res => res.json())
.then(data => {
console.log(data);
this.setState({
loading: false,
data: data.results,
pagination: {
...params.pagination,
total: 200,
// 200 is mock data, you should read it from server
// total: data.totalCount,
},
});
});
};
render() {
const { data, pagination, loading } = this.state;
return (
<Table
columns={columns}
rowKey={record => record.login.uuid}
dataSource={data}
pagination={pagination}
loading={loading}
onChange={this.handleTableChange}
/>
);
}
}
ReactDOM.render(<App />, mountNode);
Middle size table
Name | Age | Address |
---|---|---|
John Brown | 32 | New York No. 1 Lake Park |
Jim Green | 42 | London No. 1 Lake Park |
Joe Black | 32 | Sidney No. 1 Lake Park |
Small size table
Name | Age | Address |
---|---|---|
John Brown | 32 | New York No. 1 Lake Park |
Jim Green | 42 | London No. 1 Lake Park |
Joe Black | 32 | Sidney No. 1 Lake Park |
import { Table } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Age',
dataIndex: 'age',
},
{
title: 'Address',
dataIndex: 'address',
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
];
ReactDOM.render(
<div>
<h4>Middle size table</h4>
<Table columns={columns} dataSource={data} size="middle" />
<h4>Small size table</h4>
<Table columns={columns} dataSource={data} size="small" />
</div>,
mountNode,
);
Name | Cash Assets | Address |
---|---|---|
John Brown | ¥300,000.00 | New York No. 1 Lake Park |
Jim Green | ¥1,256,000.00 | London No. 1 Lake Park |
Joe Black | ¥120,000.00 | Sidney No. 1 Lake Park |
import { Table } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
render: text => <a>{text}</a>,
},
{
title: 'Cash Assets',
className: 'column-money',
dataIndex: 'money',
align: 'right',
},
{
title: 'Address',
dataIndex: 'address',
},
];
const data = [
{
key: '1',
name: 'John Brown',
money: '¥300,000.00',
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
money: '¥1,256,000.00',
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
money: '¥120,000.00',
address: 'Sidney No. 1 Lake Park',
},
];
ReactDOM.render(
<Table
columns={columns}
dataSource={data}
bordered
title={() => 'Header'}
footer={() => 'Footer'}
/>,
mountNode,
);
import { Table } from 'antd';
const columns = [
{ title: 'Name', dataIndex: 'name', key: 'name' },
{ title: 'Age', dataIndex: 'age', key: 'age' },
{ title: 'Address', dataIndex: 'address', key: 'address' },
{
title: 'Action',
dataIndex: '',
key: 'x',
render: () => <a>Delete</a>,
},
];
const data = [
{
key: 1,
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.',
},
{
key: 2,
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.',
},
{
key: 3,
name: 'Not Expandable',
age: 29,
address: 'Jiangsu No. 1 Lake Park',
description: 'This not expandable',
},
{
key: 4,
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.',
},
];
ReactDOM.render(
<Table
columns={columns}
expandable={{
expandedRowRender: record => <p style={{ margin: 0 }}>{record.description}</p>,
rowExpandable: record => record.name !== 'Not Expandable',
}}
dataSource={data}
/>,
mountNode,
);
Name | Age | Address | ||
---|---|---|---|---|
John Brown | 32 | New York No. 1 Lake Park | ||
Jim Green | 42 | London No. 1 Lake Park | ||
Not Expandable | 29 | Jiangsu No. 1 Lake Park | ||
Joe Black | 32 | Sidney No. 1 Lake Park |
import { Table } from 'antd';
const columns = [
{ title: 'Name', dataIndex: 'name', key: 'name' },
Table.EXPAND_COLUMN,
{ title: 'Age', dataIndex: 'age', key: 'age' },
Table.SELECTION_COLUMN,
{ title: 'Address', dataIndex: 'address', key: 'address' },
];
const data = [
{
key: 1,
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.',
},
{
key: 2,
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.',
},
{
key: 3,
name: 'Not Expandable',
age: 29,
address: 'Jiangsu No. 1 Lake Park',
description: 'This not expandable',
},
{
key: 4,
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.',
},
];
ReactDOM.render(
<Table
columns={columns}
rowSelection={{}}
expandable={{
expandedRowRender: record => <p style={{ margin: 0 }}>{record.description}</p>,
}}
dataSource={data}
/>,
mountNode,
);
Name | Age | Home phone | Address | |
---|---|---|---|---|
John Brown | 32 | 0571-22098909 | 18889898989 | New York No. 1 Lake Park |
Jim Green | 42 | 0571-22098333 | 18889898888 | London No. 1 Lake Park |
Joe Black | 32 | 0575-22098909 | 18900010002 | Sidney No. 1 Lake Park |
Jim Red | 18 | 18900010002 | London No. 2 Lake Park | |
Jake White |
import { Table } from 'antd';
// In the fifth row, other columns are merged into first column
// by setting it's colSpan to be 0
const sharedOnCell = (_, index) => {
if (index === 4) {
return { colSpan: 0 };
}
};
const columns = [
{
title: 'Name',
dataIndex: 'name',
render: (text, row, index) => <a>{text}</a>,
onCell: (_, index) => ({
colSpan: index < 4 ? 1 : 5,
}),
},
{
title: 'Age',
dataIndex: 'age',
onCell: sharedOnCell,
},
{
title: 'Home phone',
colSpan: 2,
dataIndex: 'tel',
onCell: (_, index) => {
if (index === 2) {
return { rowSpan: 2 };
}
// These two are merged into above cell
if (index === 3) {
return { rowSpan: 0 };
}
if (index === 4) {
return { colSpan: 0 };
}
},
},
{
title: 'Phone',
colSpan: 0,
dataIndex: 'phone',
onCell: sharedOnCell,
},
{
title: 'Address',
dataIndex: 'address',
onCell: sharedOnCell,
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
tel: '0571-22098909',
phone: 18889898989,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
tel: '0571-22098333',
phone: 18889898888,
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
tel: '0575-22098909',
phone: 18900010002,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Jim Red',
age: 18,
tel: '0575-22098909',
phone: 18900010002,
address: 'London No. 2 Lake Park',
},
{
key: '5',
name: 'Jake White',
age: 18,
tel: '0575-22098909',
phone: 18900010002,
address: 'Dublin No. 2 Lake Park',
},
];
ReactDOM.render(<Table columns={columns} dataSource={data} bordered />, mountNode);
Name | Age | Address | |
---|---|---|---|
John Brown sr. | 60 | New York No. 1 Lake Park | |
Joe Black | 32 | Sidney No. 1 Lake Park |
import { Table, Switch, Space } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: '12%',
},
{
title: 'Address',
dataIndex: 'address',
width: '30%',
key: 'address',
},
];
const data = [
{
key: 1,
name: 'John Brown sr.',
age: 60,
address: 'New York No. 1 Lake Park',
children: [
{
key: 11,
name: 'John Brown',
age: 42,
address: 'New York No. 2 Lake Park',
},
{
key: 12,
name: 'John Brown jr.',
age: 30,
address: 'New York No. 3 Lake Park',
children: [
{
key: 121,
name: 'Jimmy Brown',
age: 16,
address: 'New York No. 3 Lake Park',
},
],
},
{
key: 13,
name: 'Jim Green sr.',
age: 72,
address: 'London No. 1 Lake Park',
children: [
{
key: 131,
name: 'Jim Green',
age: 42,
address: 'London No. 2 Lake Park',
children: [
{
key: 1311,
name: 'Jim Green jr.',
age: 25,
address: 'London No. 3 Lake Park',
},
{
key: 1312,
name: 'Jimmy Green sr.',
age: 18,
address: 'London No. 4 Lake Park',
},
],
},
],
},
],
},
{
key: 2,
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
];
// rowSelection objects indicates the need for row selection
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
onSelect: (record, selected, selectedRows) => {
console.log(record, selected, selectedRows);
},
onSelectAll: (selected, selectedRows, changeRows) => {
console.log(selected, selectedRows, changeRows);
},
};
function TreeData() {
const [checkStrictly, setCheckStrictly] = React.useState(false);
return (
<>
<Space align="center" style={{ marginBottom: 16 }}>
CheckStrictly: <Switch checked={checkStrictly} onChange={setCheckStrictly} />
</Space>
<Table
columns={columns}
rowSelection={{ ...rowSelection, checkStrictly }}
dataSource={data}
/>
</>
);
}
ReactDOM.render(<TreeData />, mountNode);
Edward King 0 | 32 | London, Park Lane no. 0 |
Edward King 1 | 32 | London, Park Lane no. 1 |
Edward King 2 | 32 | London, Park Lane no. 2 |
Edward King 3 | 32 | London, Park Lane no. 3 |
Edward King 4 | 32 | London, Park Lane no. 4 |
Edward King 5 | 32 | London, Park Lane no. 5 |
Edward King 6 | 32 | London, Park Lane no. 6 |
Edward King 7 | 32 | London, Park Lane no. 7 |
Edward King 8 | 32 | London, Park Lane no. 8 |
Edward King 9 | 32 | London, Park Lane no. 9 |
Edward King 10 | 32 | London, Park Lane no. 10 |
Edward King 11 | 32 | London, Park Lane no. 11 |
Edward King 12 | 32 | London, Park Lane no. 12 |
Edward King 13 | 32 | London, Park Lane no. 13 |
Edward King 14 | 32 | London, Park Lane no. 14 |
Edward King 15 | 32 | London, Park Lane no. 15 |
Edward King 16 | 32 | London, Park Lane no. 16 |
Edward King 17 | 32 | London, Park Lane no. 17 |
Edward King 18 | 32 | London, Park Lane no. 18 |
Edward King 19 | 32 | London, Park Lane no. 19 |
Edward King 20 | 32 | London, Park Lane no. 20 |
Edward King 21 | 32 | London, Park Lane no. 21 |
Edward King 22 | 32 | London, Park Lane no. 22 |
Edward King 23 | 32 | London, Park Lane no. 23 |
Edward King 24 | 32 | London, Park Lane no. 24 |
Edward King 25 | 32 | London, Park Lane no. 25 |
Edward King 26 | 32 | London, Park Lane no. 26 |
Edward King 27 | 32 | London, Park Lane no. 27 |
Edward King 28 | 32 | London, Park Lane no. 28 |
Edward King 29 | 32 | London, Park Lane no. 29 |
Edward King 30 | 32 | London, Park Lane no. 30 |
Edward King 31 | 32 | London, Park Lane no. 31 |
Edward King 32 | 32 | London, Park Lane no. 32 |
Edward King 33 | 32 | London, Park Lane no. 33 |
Edward King 34 | 32 | London, Park Lane no. 34 |
Edward King 35 | 32 | London, Park Lane no. 35 |
Edward King 36 | 32 | London, Park Lane no. 36 |
Edward King 37 | 32 | London, Park Lane no. 37 |
Edward King 38 | 32 | London, Park Lane no. 38 |
Edward King 39 | 32 | London, Park Lane no. 39 |
Edward King 40 | 32 | London, Park Lane no. 40 |
Edward King 41 | 32 | London, Park Lane no. 41 |
Edward King 42 | 32 | London, Park Lane no. 42 |
Edward King 43 | 32 | London, Park Lane no. 43 |
Edward King 44 | 32 | London, Park Lane no. 44 |
Edward King 45 | 32 | London, Park Lane no. 45 |
Edward King 46 | 32 | London, Park Lane no. 46 |
Edward King 47 | 32 | London, Park Lane no. 47 |
Edward King 48 | 32 | London, Park Lane no. 48 |
Edward King 49 | 32 | London, Park Lane no. 49 |
import { Table } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
width: 150,
},
{
title: 'Age',
dataIndex: 'age',
width: 150,
},
{
title: 'Address',
dataIndex: 'address',
},
];
const data = [];
for (let i = 0; i < 100; i++) {
data.push({
key: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`,
});
}
ReactDOM.render(
<Table columns={columns} dataSource={data} pagination={{ pageSize: 50 }} scroll={{ y: 240 }} />,
mountNode,
);
import { Table } from 'antd';
const columns = [
{
title: 'Full Name',
width: 100,
dataIndex: 'name',
key: 'name',
fixed: 'left',
},
{
title: 'Age',
width: 100,
dataIndex: 'age',
key: 'age',
fixed: 'left',
},
{ title: 'Column 1', dataIndex: 'address', key: '1' },
{ title: 'Column 2', dataIndex: 'address', key: '2' },
{ title: 'Column 3', dataIndex: 'address', key: '3' },
{ title: 'Column 4', dataIndex: 'address', key: '4' },
{ title: 'Column 5', dataIndex: 'address', key: '5' },
{ title: 'Column 6', dataIndex: 'address', key: '6' },
{ title: 'Column 7', dataIndex: 'address', key: '7' },
{ title: 'Column 8', dataIndex: 'address', key: '8' },
{
title: 'Action',
key: 'operation',
fixed: 'right',
width: 100,
render: () => <a>action</a>,
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York Park',
},
{
key: '2',
name: 'Jim Green',
age: 40,
address: 'London Park',
},
];
ReactDOM.render(<Table columns={columns} dataSource={data} scroll={{ x: 1300 }} />, mountNode);
Edrward 0 | 32 | London Park no. 0 | London Park no. 0 | London Park no. 0 | London Park no. 0 | London Park no. 0 | London Park no. 0 | London Park no. 0 | London Park no. 0 | action |
Edrward 1 | 32 | London Park no. 1 | London Park no. 1 | London Park no. 1 | London Park no. 1 | London Park no. 1 | London Park no. 1 | London Park no. 1 | London Park no. 1 | action |
Edrward 2 | 32 | London Park no. 2 | London Park no. 2 | London Park no. 2 | London Park no. 2 | London Park no. 2 | London Park no. 2 | London Park no. 2 | London Park no. 2 | action |
Edrward 3 | 32 | London Park no. 3 | London Park no. 3 | London Park no. 3 | London Park no. 3 | London Park no. 3 | London Park no. 3 | London Park no. 3 | London Park no. 3 | action |
Edrward 4 | 32 | London Park no. 4 | London Park no. 4 | London Park no. 4 | London Park no. 4 | London Park no. 4 | London Park no. 4 | London Park no. 4 | London Park no. 4 | action |
Edrward 5 | 32 | London Park no. 5 | London Park no. 5 | London Park no. 5 | London Park no. 5 | London Park no. 5 | London Park no. 5 | London Park no. 5 | London Park no. 5 | action |
Edrward 6 | 32 | London Park no. 6 | London Park no. 6 | London Park no. 6 | London Park no. 6 | London Park no. 6 | London Park no. 6 | London Park no. 6 | London Park no. 6 | action |
Edrward 7 | 32 | London Park no. 7 | London Park no. 7 | London Park no. 7 | London Park no. 7 | London Park no. 7 | London Park no. 7 | London Park no. 7 | London Park no. 7 | action |
Edrward 8 | 32 | London Park no. 8 | London Park no. 8 | London Park no. 8 | London Park no. 8 | London Park no. 8 | London Park no. 8 | London Park no. 8 | London Park no. 8 | action |
Edrward 9 | 32 | London Park no. 9 | London Park no. 9 | London Park no. 9 | London Park no. 9 | London Park no. 9 | London Park no. 9 | London Park no. 9 | London Park no. 9 | action |
import { Table } from 'antd';
const columns = [
{
title: 'Full Name',
width: 100,
dataIndex: 'name',
key: 'name',
fixed: 'left',
},
{
title: 'Age',
width: 100,
dataIndex: 'age',
key: 'age',
fixed: 'left',
},
{
title: 'Column 1',
dataIndex: 'address',
key: '1',
width: 150,
},
{
title: 'Column 2',
dataIndex: 'address',
key: '2',
width: 150,
},
{
title: 'Column 3',
dataIndex: 'address',
key: '3',
width: 150,
},
{
title: 'Column 4',
dataIndex: 'address',
key: '4',
width: 150,
},
{
title: 'Column 5',
dataIndex: 'address',
key: '5',
width: 150,
},
{
title: 'Column 6',
dataIndex: 'address',
key: '6',
width: 150,
},
{
title: 'Column 7',
dataIndex: 'address',
key: '7',
width: 150,
},
{ title: 'Column 8', dataIndex: 'address', key: '8' },
{
title: 'Action',
key: 'operation',
fixed: 'right',
width: 100,
render: () => <a>action</a>,
},
];
const data = [];
for (let i = 0; i < 100; i++) {
data.push({
key: i,
name: `Edrward ${i}`,
age: 32,
address: `London Park no. ${i}`,
});
}
ReactDOM.render(
<Table columns={columns} dataSource={data} scroll={{ x: 1500, y: 300 }} />,
mountNode,
);
John Brown | 1 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 2 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 3 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 4 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 5 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 6 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 7 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 8 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 9 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 10 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
import { Table } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
width: 100,
fixed: 'left',
filters: [
{
text: 'Joe',
value: 'Joe',
},
{
text: 'John',
value: 'John',
},
],
onFilter: (value, record) => record.name.indexOf(value) === 0,
},
{
title: 'Other',
children: [
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: 150,
sorter: (a, b) => a.age - b.age,
},
{
title: 'Address',
children: [
{
title: 'Street',
dataIndex: 'street',
key: 'street',
width: 150,
},
{
title: 'Block',
children: [
{
title: 'Building',
dataIndex: 'building',
key: 'building',
width: 100,
},
{
title: 'Door No.',
dataIndex: 'number',
key: 'number',
width: 100,
},
],
},
],
},
],
},
{
title: 'Company',
children: [
{
title: 'Company Address',
dataIndex: 'companyAddress',
key: 'companyAddress',
width: 200,
},
{
title: 'Company Name',
dataIndex: 'companyName',
key: 'companyName',
},
],
},
{
title: 'Gender',
dataIndex: 'gender',
key: 'gender',
width: 80,
fixed: 'right',
},
];
const data = [];
for (let i = 0; i < 100; i++) {
data.push({
key: i,
name: 'John Brown',
age: i + 1,
street: 'Lake Park',
building: 'C',
number: 2035,
companyAddress: 'Lake Street 42',
companyName: 'SoftLake Co',
gender: 'M',
});
}
ReactDOM.render(
<Table
columns={columns}
dataSource={data}
bordered
size="middle"
scroll={{ x: 'calc(700px + 50%)', y: 240 }}
/>,
mountNode,
);
import React, { useContext, useState, useEffect, useRef } from 'react';
import { Table, Input, Button, Popconfirm, Form } from 'antd';
import { FormInstance } from 'antd/lib/form';
const EditableContext = React.createContext<FormInstance<any> | null>(null);
interface Item {
key: string;
name: string;
age: string;
address: string;
}
interface EditableRowProps {
index: number;
}
const EditableRow: React.FC<EditableRowProps> = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
interface EditableCellProps {
title: React.ReactNode;
editable: boolean;
children: React.ReactNode;
dataIndex: keyof Item;
record: Item;
handleSave: (record: Item) => void;
}
const EditableCell: React.FC<EditableCellProps> = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
...restProps
}) => {
const [editing, setEditing] = useState(false);
const inputRef = useRef<Input>(null);
const form = useContext(EditableContext)!;
useEffect(() => {
if (editing) {
inputRef.current!.focus();
}
}, [editing]);
const toggleEdit = () => {
setEditing(!editing);
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
};
const save = async () => {
try {
const values = await form.validateFields();
toggleEdit();
handleSave({ ...record, ...values });
} catch (errInfo) {
console.log('Save failed:', errInfo);
}
};
let childNode = children;
if (editable) {
childNode = editing ? (
<Form.Item
style={{ margin: 0 }}
name={dataIndex}
rules={[
{
required: true,
message: `${title} is required.`,
},
]}
>
<Input ref={inputRef} onPressEnter={save} onBlur={save} />
</Form.Item>
) : (
<div className="editable-cell-value-wrap" style={{ paddingRight: 24 }} onClick={toggleEdit}>
{children}
</div>
);
}
return <td {...restProps}>{childNode}</td>;
};
type EditableTableProps = Parameters<typeof Table>[0];
interface DataType {
key: React.Key;
name: string;
age: string;
address: string;
}
interface EditableTableState {
dataSource: DataType[];
count: number;
}
type ColumnTypes = Exclude<EditableTableProps['columns'], undefined>;
class EditableTable extends React.Component<EditableTableProps, EditableTableState> {
columns: (ColumnTypes[number] & { editable?: boolean; dataIndex: string })[];
constructor(props: EditableTableProps) {
super(props);
this.columns = [
{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true,
},
{
title: 'age',
dataIndex: 'age',
},
{
title: 'address',
dataIndex: 'address',
},
{
title: 'operation',
dataIndex: 'operation',
render: (_, record: { key: React.Key }) =>
this.state.dataSource.length >= 1 ? (
<Popconfirm title="Sure to delete?" onConfirm={() => this.handleDelete(record.key)}>
<a>Delete</a>
</Popconfirm>
) : null,
},
];
this.state = {
dataSource: [
{
key: '0',
name: 'Edward King 0',
age: '32',
address: 'London, Park Lane no. 0',
},
{
key: '1',
name: 'Edward King 1',
age: '32',
address: 'London, Park Lane no. 1',
},
],
count: 2,
};
}
handleDelete = (key: React.Key) => {
const dataSource = [...this.state.dataSource];
this.setState({ dataSource: dataSource.filter(item => item.key !== key) });
};
handleAdd = () => {
const { count, dataSource } = this.state;
const newData: DataType = {
key: count,
name: `Edward King ${count}`,
age: '32',
address: `London, Park Lane no. ${count}`,
};
this.setState({
dataSource: [...dataSource, newData],
count: count + 1,
});
};
handleSave = (row: DataType) => {
const newData = [...this.state.dataSource];
const index = newData.findIndex(item => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
this.setState({ dataSource: newData });
};
render() {
const { dataSource } = this.state;
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
const columns = this.columns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: DataType) => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave: this.handleSave,
}),
};
});
return (
<div>
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
Add a row
</Button>
<Table
components={components}
rowClassName={() => 'editable-row'}
bordered
dataSource={dataSource}
columns={columns as ColumnTypes}
/>
</div>
);
}
}
ReactDOM.render(<EditableTable />, mountNode);
.editable-cell {
position: relative;
}
.editable-cell-value-wrap {
padding: 5px 12px;
cursor: pointer;
}
.editable-row:hover .editable-cell-value-wrap {
padding: 4px 11px;
border: 1px solid #d9d9d9;
border-radius: 2px;
}
[data-theme='dark'] .editable-row:hover .editable-cell-value-wrap {
border: 1px solid #434343;
}
name | age | address | operation |
---|---|---|---|
Edrward 0 | 32 | London Park no. 0 | Edit |
Edrward 1 | 32 | London Park no. 1 | Edit |
Edrward 2 | 32 | London Park no. 2 | Edit |
Edrward 3 | 32 | London Park no. 3 | Edit |
Edrward 4 | 32 | London Park no. 4 | Edit |
Edrward 5 | 32 | London Park no. 5 | Edit |
Edrward 6 | 32 | London Park no. 6 | Edit |
Edrward 7 | 32 | London Park no. 7 | Edit |
Edrward 8 | 32 | London Park no. 8 | Edit |
Edrward 9 | 32 | London Park no. 9 | Edit |
import React, { useState } from 'react';
import { Table, Input, InputNumber, Popconfirm, Form, Typography } from 'antd';
interface Item {
key: string;
name: string;
age: number;
address: string;
}
const originData: Item[] = [];
for (let i = 0; i < 100; i++) {
originData.push({
key: i.toString(),
name: `Edrward ${i}`,
age: 32,
address: `London Park no. ${i}`,
});
}
interface EditableCellProps extends React.HTMLAttributes<HTMLElement> {
editing: boolean;
dataIndex: string;
title: any;
inputType: 'number' | 'text';
record: Item;
index: number;
children: React.ReactNode;
}
const EditableCell: React.FC<EditableCellProps> = ({
editing,
dataIndex,
title,
inputType,
record,
index,
children,
...restProps
}) => {
const inputNode = inputType === 'number' ? <InputNumber /> : <Input />;
return (
<td {...restProps}>
{editing ? (
<Form.Item
name={dataIndex}
style={{ margin: 0 }}
rules={[
{
required: true,
message: `Please Input ${title}!`,
},
]}
>
{inputNode}
</Form.Item>
) : (
children
)}
</td>
);
};
const EditableTable = () => {
const [form] = Form.useForm();
const [data, setData] = useState(originData);
const [editingKey, setEditingKey] = useState('');
const isEditing = (record: Item) => record.key === editingKey;
const edit = (record: Partial<Item> & { key: React.Key }) => {
form.setFieldsValue({ name: '', age: '', address: '', ...record });
setEditingKey(record.key);
};
const cancel = () => {
setEditingKey('');
};
const save = async (key: React.Key) => {
try {
const row = (await form.validateFields()) as Item;
const newData = [...data];
const index = newData.findIndex(item => key === item.key);
if (index > -1) {
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
setData(newData);
setEditingKey('');
} else {
newData.push(row);
setData(newData);
setEditingKey('');
}
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
}
};
const columns = [
{
title: 'name',
dataIndex: 'name',
width: '25%',
editable: true,
},
{
title: 'age',
dataIndex: 'age',
width: '15%',
editable: true,
},
{
title: 'address',
dataIndex: 'address',
width: '40%',
editable: true,
},
{
title: 'operation',
dataIndex: 'operation',
render: (_: any, record: Item) => {
const editable = isEditing(record);
return editable ? (
<span>
<Typography.Link onClick={() => save(record.key)} style={{ marginRight: 8 }}>
Save
</Typography.Link>
<Popconfirm title="Sure to cancel?" onConfirm={cancel}>
<a>Cancel</a>
</Popconfirm>
</span>
) : (
<Typography.Link disabled={editingKey !== ''} onClick={() => edit(record)}>
Edit
</Typography.Link>
);
},
},
];
const mergedColumns = columns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: Item) => ({
record,
inputType: col.dataIndex === 'age' ? 'number' : 'text',
dataIndex: col.dataIndex,
title: col.title,
editing: isEditing(record),
}),
};
});
return (
<Form form={form} component={false}>
<Table
components={{
body: {
cell: EditableCell,
},
}}
bordered
dataSource={data}
columns={mergedColumns}
rowClassName="editable-row"
pagination={{
onChange: cancel,
}}
/>
</Form>
);
};
ReactDOM.render(<EditableTable />, mountNode);
.editable-row .ant-form-item-explain {
position: absolute;
top: 100%;
font-size: 12px;
}
import { Table, Badge, Menu, Dropdown, Space } from 'antd';
import { DownOutlined } from '@ant-design/icons';
const menu = (
<Menu>
<Menu.Item>Action 1</Menu.Item>
<Menu.Item>Action 2</Menu.Item>
</Menu>
);
function NestedTable() {
const expandedRowRender = () => {
const columns = [
{ title: 'Date', dataIndex: 'date', key: 'date' },
{ title: 'Name', dataIndex: 'name', key: 'name' },
{
title: 'Status',
key: 'state',
render: () => (
<span>
<Badge status="success" />
Finished
</span>
),
},
{ title: 'Upgrade Status', dataIndex: 'upgradeNum', key: 'upgradeNum' },
{
title: 'Action',
dataIndex: 'operation',
key: 'operation',
render: () => (
<Space size="middle">
<a>Pause</a>
<a>Stop</a>
<Dropdown overlay={menu}>
<a>
More <DownOutlined />
</a>
</Dropdown>
</Space>
),
},
];
const data = [];
for (let i = 0; i < 3; ++i) {
data.push({
key: i,
date: '2014-12-24 23:12:00',
name: 'This is production name',
upgradeNum: 'Upgraded: 56',
});
}
return <Table columns={columns} dataSource={data} pagination={false} />;
};
const columns = [
{ title: 'Name', dataIndex: 'name', key: 'name' },
{ title: 'Platform', dataIndex: 'platform', key: 'platform' },
{ title: 'Version', dataIndex: 'version', key: 'version' },
{ title: 'Upgraded', dataIndex: 'upgradeNum', key: 'upgradeNum' },
{ title: 'Creator', dataIndex: 'creator', key: 'creator' },
{ title: 'Date', dataIndex: 'createdAt', key: 'createdAt' },
{ title: 'Action', key: 'operation', render: () => <a>Publish</a> },
];
const data = [];
for (let i = 0; i < 3; ++i) {
data.push({
key: i,
name: 'Screem',
platform: 'iOS',
version: '10.3.4.5654',
upgradeNum: 500,
creator: 'Jack',
createdAt: '2014-12-24 23:12:00',
});
}
return (
<Table
className="components-table-demo-nested"
columns={columns}
expandable={{ expandedRowRender }}
dataSource={data}
/>
);
}
ReactDOM.render(<NestedTable />, mountNode);
Name | Age | Address |
---|---|---|
John Brown | 32 | New York No. 1 Lake Park |
Jim Green | 42 | London No. 1 Lake Park |
Joe Black | 32 | Sidney No. 1 Lake Park |
import React, { useState, useCallback, useRef } from 'react';
import { Table } from 'antd';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import update from 'immutability-helper';
const type = 'DraggableBodyRow';
const DraggableBodyRow = ({ index, moveRow, className, style, ...restProps }) => {
const ref = useRef();
const [{ isOver, dropClassName }, drop] = useDrop({
accept: type,
collect: monitor => {
const { index: dragIndex } = monitor.getItem() || {};
if (dragIndex === index) {
return {};
}
return {
isOver: monitor.isOver(),
dropClassName: dragIndex < index ? ' drop-over-downward' : ' drop-over-upward',
};
},
drop: item => {
moveRow(item.index, index);
},
});
const [, drag] = useDrag({
type,
item: { index },
collect: monitor => ({
isDragging: monitor.isDragging(),
}),
});
drop(drag(ref));
return (
<tr
ref={ref}
className={`${className}${isOver ? dropClassName : ''}`}
style={{ cursor: 'move', ...style }}
{...restProps}
/>
);
};
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
];
const DragSortingTable: React.FC = () => {
const [data, setData] = useState([
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
]);
const components = {
body: {
row: DraggableBodyRow,
},
};
const moveRow = useCallback(
(dragIndex, hoverIndex) => {
const dragRow = data[dragIndex];
setData(
update(data, {
$splice: [
[dragIndex, 1],
[hoverIndex, 0, dragRow],
],
}),
);
},
[data],
);
return (
<DndProvider backend={HTML5Backend}>
<Table
columns={columns}
dataSource={data}
components={components}
onRow={(record, index) => ({
index,
moveRow,
})}
/>
</DndProvider>
);
};
ReactDOM.render(<DragSortingTable />, mountNode);
#components-table-demo-drag-sorting tr.drop-over-downward td {
border-bottom: 2px dashed #1890ff;
}
#components-table-demo-drag-sorting tr.drop-over-upward td {
border-top: 2px dashed #1890ff;
}
Sort | Name | Age | Address |
---|---|---|---|
John Brown | 32 | New York No. 1 Lake Park | |
Jim Green | 42 | London No. 1 Lake Park | |
Joe Black | 32 | Sidney No. 1 Lake Park |
import { Table } from 'antd';
import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
import { MenuOutlined } from '@ant-design/icons';
import { arrayMoveImmutable } from 'array-move';
const DragHandle = SortableHandle(() => <MenuOutlined style={{ cursor: 'grab', color: '#999' }} />);
const columns = [
{
title: 'Sort',
dataIndex: 'sort',
width: 30,
className: 'drag-visible',
render: () => <DragHandle />,
},
{
title: 'Name',
dataIndex: 'name',
className: 'drag-visible',
},
{
title: 'Age',
dataIndex: 'age',
},
{
title: 'Address',
dataIndex: 'address',
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
index: 0,
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
index: 1,
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
index: 2,
},
];
const SortableItem = SortableElement(props => <tr {...props} />);
const SortableBody = SortableContainer(props => <tbody {...props} />);
class SortableTable extends React.Component {
state = {
dataSource: data,
};
onSortEnd = ({ oldIndex, newIndex }) => {
const { dataSource } = this.state;
if (oldIndex !== newIndex) {
const newData = arrayMoveImmutable([].concat(dataSource), oldIndex, newIndex).filter(
el => !!el,
);
console.log('Sorted items: ', newData);
this.setState({ dataSource: newData });
}
};
DraggableContainer = props => (
<SortableBody
useDragHandle
disableAutoscroll
helperClass="row-dragging"
onSortEnd={this.onSortEnd}
{...props}
/>
);
DraggableBodyRow = ({ className, style, ...restProps }) => {
const { dataSource } = this.state;
// function findIndex base on Table rowKey props and should always be a right array index
const index = dataSource.findIndex(x => x.index === restProps['data-row-key']);
return <SortableItem index={index} {...restProps} />;
};
render() {
const { dataSource } = this.state;
return (
<Table
pagination={false}
dataSource={dataSource}
columns={columns}
rowKey="index"
components={{
body: {
wrapper: this.DraggableContainer,
row: this.DraggableBodyRow,
},
}}
/>
);
}
}
ReactDOM.render(<SortableTable />, mountNode);
.row-dragging {
background: #fafafa;
border: 1px solid #ccc;
}
.row-dragging td {
padding: 16px;
}
.row-dragging .drag-visible {
visibility: visible;
}
Name | Age | Address | Long Column Long Column Long Column | Long Column Long Column | Long Column |
---|---|---|---|---|---|
John Brown | 32 | New York No. 1 Lake Park, New York No. 1 Lake Park | New York No. 1 Lake Park, New York No. 1 Lake Park | New York No. 1 Lake Park, New York No. 1 Lake Park | New York No. 1 Lake Park, New York No. 1 Lake Park |
Jim Green | 42 | London No. 2 Lake Park, London No. 2 Lake Park | London No. 2 Lake Park, London No. 2 Lake Park | London No. 2 Lake Park, London No. 2 Lake Park | London No. 2 Lake Park, London No. 2 Lake Park |
Joe Black | 32 | Sidney No. 1 Lake Park, Sidney No. 1 Lake Park | Sidney No. 1 Lake Park, Sidney No. 1 Lake Park | Sidney No. 1 Lake Park, Sidney No. 1 Lake Park | Sidney No. 1 Lake Park, Sidney No. 1 Lake Park |
import { Table } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: text => <a>{text}</a>,
width: 150,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: 80,
},
{
title: 'Address',
dataIndex: 'address',
key: 'address 1',
ellipsis: true,
},
{
title: 'Long Column Long Column Long Column',
dataIndex: 'address',
key: 'address 2',
ellipsis: true,
},
{
title: 'Long Column Long Column',
dataIndex: 'address',
key: 'address 3',
ellipsis: true,
},
{
title: 'Long Column',
dataIndex: 'address',
key: 'address 4',
ellipsis: true,
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park, New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 2 Lake Park, London No. 2 Lake Park',
tags: ['loser'],
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park, Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
ReactDOM.render(<Table columns={columns} dataSource={data} />, mountNode);
Name | Age | Address | Long Column Long Column Long Column | Long Column Long Column | Long Column |
---|---|---|---|---|---|
John Brown | 32 | New York No. 1 Lake Park, New York No. 1 Lake Park | New York No. 1 Lake Park, New York No. 1 Lake Park | New York No. 1 Lake Park, New York No. 1 Lake Park | New York No. 1 Lake Park, New York No. 1 Lake Park |
Jim Green | 42 | London No. 2 Lake Park, London No. 2 Lake Park | London No. 2 Lake Park, London No. 2 Lake Park | London No. 2 Lake Park, London No. 2 Lake Park | London No. 2 Lake Park, London No. 2 Lake Park |
Joe Black | 32 | Sidney No. 1 Lake Park, Sidney No. 1 Lake Park | Sidney No. 1 Lake Park, Sidney No. 1 Lake Park | Sidney No. 1 Lake Park, Sidney No. 1 Lake Park | Sidney No. 1 Lake Park, Sidney No. 1 Lake Park |
import { Table, Tooltip } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: text => <a>{text}</a>,
width: 150,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: 80,
},
{
title: 'Address',
dataIndex: 'address',
key: 'address 1',
ellipsis: {
showTitle: false,
},
render: address => (
<Tooltip placement="topLeft" title={address}>
{address}
</Tooltip>
),
},
{
title: 'Long Column Long Column Long Column',
dataIndex: 'address',
key: 'address 2',
ellipsis: {
showTitle: false,
},
render: address => (
<Tooltip placement="topLeft" title={address}>
{address}
</Tooltip>
),
},
{
title: 'Long Column Long Column',
dataIndex: 'address',
key: 'address 3',
ellipsis: {
showTitle: false,
},
render: address => (
<Tooltip placement="topLeft" title={address}>
{address}
</Tooltip>
),
},
{
title: 'Long Column',
dataIndex: 'address',
key: 'address 4',
ellipsis: {
showTitle: false,
},
render: address => (
<Tooltip placement="topLeft" title={address}>
{address}
</Tooltip>
),
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park, New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 2 Lake Park, London No. 2 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park, Sidney No. 1 Lake Park',
},
];
ReactDOM.render(<Table columns={columns} dataSource={data} />, mountNode);
Name | Borrow | Repayment |
---|---|---|
John Brown | 10 | 33 |
Jim Green | 100 | 0 |
Joe Black | 10 | 10 |
Jim Red | 75 | 45 |
Total | 195 | 88 |
Balance | 107 |
Light | Everything that has a beginning, has an end. |
Bamboo | Everything that has a beginning, has an end. |
Little | Everything that has a beginning, has an end. |
Light | Everything that has a beginning, has an end. |
Bamboo | Everything that has a beginning, has an end. |
Little | Everything that has a beginning, has an end. |
Light | Everything that has a beginning, has an end. |
Bamboo | Everything that has a beginning, has an end. |
Little | Everything that has a beginning, has an end. |
Light | Everything that has a beginning, has an end. |
Bamboo | Everything that has a beginning, has an end. |
Little | Everything that has a beginning, has an end. |
Light | Everything that has a beginning, has an end. |
Bamboo | Everything that has a beginning, has an end. |
Little | Everything that has a beginning, has an end. |
Light | Everything that has a beginning, has an end. |
Bamboo | Everything that has a beginning, has an end. |
Little | Everything that has a beginning, has an end. |
Light | Everything that has a beginning, has an end. |
Bamboo | Everything that has a beginning, has an end. |
import { Table, Typography } from 'antd';
const { Text } = Typography;
const columns = [
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Borrow',
dataIndex: 'borrow',
},
{
title: 'Repayment',
dataIndex: 'repayment',
},
];
const data = [
{
key: '1',
name: 'John Brown',
borrow: 10,
repayment: 33,
},
{
key: '2',
name: 'Jim Green',
borrow: 100,
repayment: 0,
},
{
key: '3',
name: 'Joe Black',
borrow: 10,
repayment: 10,
},
{
key: '4',
name: 'Jim Red',
borrow: 75,
repayment: 45,
},
];
const fixedColumns = [
{
title: 'Name',
dataIndex: 'name',
fixed: true,
width: 100,
},
{
title: 'Description',
dataIndex: 'description',
},
];
const fixedData = [];
for (let i = 0; i < 20; i += 1) {
fixedData.push({
key: i,
name: ['Light', 'Bamboo', 'Little'][i % 3],
description: 'Everything that has a beginning, has an end.',
});
}
ReactDOM.render(
<>
<Table
columns={columns}
dataSource={data}
pagination={false}
bordered
summary={pageData => {
let totalBorrow = 0;
let totalRepayment = 0;
pageData.forEach(({ borrow, repayment }) => {
totalBorrow += borrow;
totalRepayment += repayment;
});
return (
<>
<Table.Summary.Row>
<Table.Summary.Cell>Total</Table.Summary.Cell>
<Table.Summary.Cell>
<Text type="danger">{totalBorrow}</Text>
</Table.Summary.Cell>
<Table.Summary.Cell>
<Text>{totalRepayment}</Text>
</Table.Summary.Cell>
</Table.Summary.Row>
<Table.Summary.Row>
<Table.Summary.Cell>Balance</Table.Summary.Cell>
<Table.Summary.Cell colSpan={2}>
<Text type="danger">{totalBorrow - totalRepayment}</Text>
</Table.Summary.Cell>
</Table.Summary.Row>
</>
);
}}
/>
<br />
<Table
columns={fixedColumns}
dataSource={fixedData}
pagination={false}
scroll={{ x: 2000, y: 500 }}
bordered
summary={() => (
<Table.Summary fixed>
<Table.Summary.Row>
<Table.Summary.Cell index={0}>Summary</Table.Summary.Cell>
<Table.Summary.Cell index={1}>This is a summary content</Table.Summary.Cell>
</Table.Summary.Row>
</Table.Summary>
)}
/>
</>,
mountNode,
);
import React, { useState, useEffect, useRef } from 'react';
import { VariableSizeGrid as Grid } from 'react-window';
import ResizeObserver from 'rc-resize-observer';
import classNames from 'classnames';
import { Table } from 'antd';
function VirtualTable(props: Parameters<typeof Table>[0]) {
const { columns, scroll } = props;
const [tableWidth, setTableWidth] = useState(0);
const widthColumnCount = columns!.filter(({ width }) => !width).length;
const mergedColumns = columns!.map(column => {
if (column.width) {
return column;
}
return {
...column,
width: Math.floor(tableWidth / widthColumnCount),
};
});
const gridRef = useRef<any>();
const [connectObject] = useState<any>(() => {
const obj = {};
Object.defineProperty(obj, 'scrollLeft', {
get: () => null,
set: (scrollLeft: number) => {
if (gridRef.current) {
gridRef.current.scrollTo({ scrollLeft });
}
},
});
return obj;
});
const resetVirtualGrid = () => {
gridRef.current.resetAfterIndices({
columnIndex: 0,
shouldForceUpdate: true,
});
};
useEffect(() => resetVirtualGrid, [tableWidth]);
const renderVirtualList = (rawData: object[], { scrollbarSize, ref, onScroll }: any) => {
ref.current = connectObject;
const totalHeight = rawData.length * 54;
return (
<Grid
ref={gridRef}
className="virtual-grid"
columnCount={mergedColumns.length}
columnWidth={(index: number) => {
const { width } = mergedColumns[index];
return totalHeight > scroll!.y! && index === mergedColumns.length - 1
? (width as number) - scrollbarSize - 1
: (width as number);
}}
height={scroll!.y as number}
rowCount={rawData.length}
rowHeight={() => 54}
width={tableWidth}
onScroll={({ scrollLeft }: { scrollLeft: number }) => {
onScroll({ scrollLeft });
}}
>
{({
columnIndex,
rowIndex,
style,
}: {
columnIndex: number;
rowIndex: number;
style: React.CSSProperties;
}) => (
<div
className={classNames('virtual-table-cell', {
'virtual-table-cell-last': columnIndex === mergedColumns.length - 1,
})}
style={style}
>
{(rawData[rowIndex] as any)[(mergedColumns as any)[columnIndex].dataIndex]}
</div>
)}
</Grid>
);
};
return (
<ResizeObserver
onResize={({ width }) => {
setTableWidth(width);
}}
>
<Table
{...props}
className="virtual-table"
columns={mergedColumns}
pagination={false}
components={{
body: renderVirtualList,
}}
/>
</ResizeObserver>
);
}
// Usage
const columns = [
{ title: 'A', dataIndex: 'key', width: 150 },
{ title: 'B', dataIndex: 'key' },
{ title: 'C', dataIndex: 'key' },
{ title: 'D', dataIndex: 'key' },
{ title: 'E', dataIndex: 'key', width: 200 },
{ title: 'F', dataIndex: 'key', width: 100 },
];
const data = Array.from({ length: 100000 }, (_, key) => ({ key }));
ReactDOM.render(
<VirtualTable columns={columns} dataSource={data} scroll={{ y: 300, x: '100vw' }} />,
mountNode,
);
Name (all screens) |
---|
John Brown |
import { Table } from 'antd';
const columns = [
{
title: 'Name (all screens)',
dataIndex: 'name',
key: 'name',
render: text => <a>{text}</a>,
},
{
title: 'Age (medium screen or bigger)',
dataIndex: 'age',
key: 'age',
responsive: ['md'],
},
{
title: 'Address (large screen or bigger)',
dataIndex: 'address',
key: 'address',
responsive: ['lg'],
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
];
ReactDOM.render(<Table columns={columns} dataSource={data} />, mountNode);
Name | Age | Address | Tags | Action |
---|---|---|---|---|
John Brown | 32 | New York No. 1 Lake Park | NICEDEVELOPER | |
Jim Green | 42 | London No. 1 Lake Park | LOSER | |
Joe Black | 32 | Sidney No. 1 Lake Park | COOLTEACHER |
import { Table, Tag, Radio, Space } from 'antd';
const topOptions = [
{ label: 'topLeft', value: 'topLeft' },
{ label: 'topCenter', value: 'topCenter' },
{ label: 'topRight', value: 'topRight' },
{ label: 'none', value: 'none' },
];
const bottomOptions = [
{ label: 'bottomLeft', value: 'bottomLeft' },
{ label: 'bottomCenter', value: 'bottomCenter' },
{ label: 'bottomRight', value: 'bottomRight' },
{ label: 'none', value: 'none' },
];
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: text => <a>{text}</a>,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
{
title: 'Tags',
key: 'tags',
dataIndex: 'tags',
render: tags => (
<span>
{tags.map(tag => {
let color = tag.length > 5 ? 'geekblue' : 'green';
if (tag === 'loser') {
color = 'volcano';
}
return (
<Tag color={color} key={tag}>
{tag.toUpperCase()}
</Tag>
);
})}
</span>
),
},
{
title: 'Action',
key: 'action',
render: (text, record) => (
<Space size="middle">
<a>Invite {record.name}</a>
<a>Delete</a>
</Space>
),
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
class Demo extends React.Component {
state = {
top: 'topLeft',
bottom: 'bottomRight',
};
render() {
return (
<div>
<div>
<Radio.Group
style={{ marginBottom: 10 }}
options={topOptions}
value={this.state.top}
onChange={e => {
this.setState({ top: e.target.value });
}}
/>
</div>
<Radio.Group
style={{ marginBottom: 10 }}
options={bottomOptions}
value={this.state.bottom}
onChange={e => {
this.setState({ bottom: e.target.value });
}}
/>
<Table
columns={columns}
pagination={{ position: [this.state.top, this.state.bottom] }}
dataSource={data}
/>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
import { Table, Switch } from 'antd';
const columns = [
{
title: 'Full Name',
width: 100,
dataIndex: 'name',
key: 'name',
fixed: 'left',
},
{
title: 'Age',
width: 100,
dataIndex: 'age',
key: 'age',
fixed: 'left',
},
{
title: 'Column 1',
dataIndex: 'address',
key: '1',
width: 150,
},
{
title: 'Column 2',
dataIndex: 'address',
key: '2',
width: 150,
},
{
title: 'Column 3',
dataIndex: 'address',
key: '3',
width: 150,
},
{
title: 'Column 4',
dataIndex: 'address',
key: '4',
width: 150,
},
{
title: 'Column 5',
dataIndex: 'address',
key: '5',
width: 150,
},
{
title: 'Column 6',
dataIndex: 'address',
key: '6',
width: 150,
},
{
title: 'Column 7',
dataIndex: 'address',
key: '7',
width: 150,
},
{ title: 'Column 8', dataIndex: 'address', key: '8' },
{
title: 'Action',
key: 'operation',
fixed: 'right',
width: 100,
render: () => <a>action</a>,
},
];
const data = [];
for (let i = 0; i < 100; i++) {
data.push({
key: i,
name: `Edrward ${i}`,
age: 32,
address: `London Park no. ${i}`,
});
}
const Demo = () => {
const [fixedTop, setFixedTop] = React.useState(false);
return (
<Table
columns={columns}
dataSource={data}
scroll={{ x: 1500 }}
summary={pageData => (
<Table.Summary fixed={fixedTop ? 'top' : 'bottom'}>
<Table.Summary.Row>
<Table.Summary.Cell index={0} colSpan={2}>
<Switch
checkedChildren="Fixed Top"
unCheckedChildren="Fixed Top"
checked={fixedTop}
onChange={() => {
setFixedTop(!fixedTop);
}}
/>
</Table.Summary.Cell>
<Table.Summary.Cell index={2} colSpan={8}>
Scroll Context
</Table.Summary.Cell>
<Table.Summary.Cell index={10}>Fix Right</Table.Summary.Cell>
</Table.Summary.Row>
</Table.Summary>
)}
sticky
/>
);
};
ReactDOM.render(<Demo />, mountNode);
Name | Age | Address | Action | ||
---|---|---|---|---|---|
John Brown | 12 | New York No. 1 Lake Park | |||
John Brown | 22 | New York No. 2 Lake Park | |||
John Brown | 32 | New York No. 3 Lake Park | |||
John Brown | 42 | New York No. 4 Lake Park | |||
John Brown | 52 | New York No. 5 Lake Park | |||
John Brown | 62 | New York No. 6 Lake Park | |||
John Brown | 72 | New York No. 7 Lake Park | |||
John Brown | 82 | New York No. 8 Lake Park | |||
John Brown | 92 | New York No. 9 Lake Park | |||
John Brown | 102 | New York No. 10 Lake Park |
import { Table, Switch, Radio, Form, Space } from 'antd';
import { DownOutlined } from '@ant-design/icons';
const columns = [
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Age',
dataIndex: 'age',
sorter: (a, b) => a.age - b.age,
},
{
title: 'Address',
dataIndex: 'address',
filters: [
{
text: 'London',
value: 'London',
},
{
text: 'New York',
value: 'New York',
},
],
onFilter: (value, record) => record.address.indexOf(value) === 0,
},
{
title: 'Action',
key: 'action',
sorter: true,
render: () => (
<Space size="middle">
<a>Delete</a>
<a className="ant-dropdown-link">
More actions <DownOutlined />
</a>
</Space>
),
},
];
const data = [];
for (let i = 1; i <= 10; i++) {
data.push({
key: i,
name: 'John Brown',
age: `${i}2`,
address: `New York No. ${i} Lake Park`,
description: `My name is John Brown, I am ${i}2 years old, living in New York No. ${i} Lake Park.`,
});
}
const expandable = { expandedRowRender: record => <p>{record.description}</p> };
const title = () => 'Here is title';
const showHeader = true;
const footer = () => 'Here is footer';
const pagination = { position: 'bottom' };
class Demo extends React.Component {
state = {
bordered: false,
loading: false,
pagination,
size: 'default',
expandable,
title: undefined,
showHeader,
footer,
rowSelection: {},
scroll: undefined,
hasData: true,
tableLayout: undefined,
top: 'none',
bottom: 'bottomRight',
};
handleToggle = prop => enable => {
this.setState({ [prop]: enable });
};
handleSizeChange = e => {
this.setState({ size: e.target.value });
};
handleTableLayoutChange = e => {
this.setState({ tableLayout: e.target.value });
};
handleExpandChange = enable => {
this.setState({ expandable: enable ? expandable : undefined });
};
handleEllipsisChange = enable => {
this.setState({ ellipsis: enable });
};
handleTitleChange = enable => {
this.setState({ title: enable ? title : undefined });
};
handleHeaderChange = enable => {
this.setState({ showHeader: enable ? showHeader : false });
};
handleFooterChange = enable => {
this.setState({ footer: enable ? footer : undefined });
};
handleRowSelectionChange = enable => {
this.setState({ rowSelection: enable ? {} : undefined });
};
handleYScrollChange = enable => {
this.setState({ yScroll: enable });
};
handleXScrollChange = e => {
this.setState({ xScroll: e.target.value });
};
handleDataChange = hasData => {
this.setState({ hasData });
};
render() {
const { xScroll, yScroll, ...state } = this.state;
const scroll = {};
if (yScroll) {
scroll.y = 240;
}
if (xScroll) {
scroll.x = '100vw';
}
const tableColumns = columns.map(item => ({ ...item, ellipsis: state.ellipsis }));
if (xScroll === 'fixed') {
tableColumns[0].fixed = true;
tableColumns[tableColumns.length - 1].fixed = 'right';
}
return (
<>
<Form
layout="inline"
className="components-table-demo-control-bar"
style={{ marginBottom: 16 }}
>
<Form.Item label="Bordered">
<Switch checked={state.bordered} onChange={this.handleToggle('bordered')} />
</Form.Item>
<Form.Item label="loading">
<Switch checked={state.loading} onChange={this.handleToggle('loading')} />
</Form.Item>
<Form.Item label="Title">
<Switch checked={!!state.title} onChange={this.handleTitleChange} />
</Form.Item>
<Form.Item label="Column Header">
<Switch checked={!!state.showHeader} onChange={this.handleHeaderChange} />
</Form.Item>
<Form.Item label="Footer">
<Switch checked={!!state.footer} onChange={this.handleFooterChange} />
</Form.Item>
<Form.Item label="Expandable">
<Switch checked={!!state.expandable} onChange={this.handleExpandChange} />
</Form.Item>
<Form.Item label="Checkbox">
<Switch checked={!!state.rowSelection} onChange={this.handleRowSelectionChange} />
</Form.Item>
<Form.Item label="Fixed Header">
<Switch checked={!!yScroll} onChange={this.handleYScrollChange} />
</Form.Item>
<Form.Item label="Has Data">
<Switch checked={!!state.hasData} onChange={this.handleDataChange} />
</Form.Item>
<Form.Item label="Ellipsis">
<Switch checked={!!state.ellipsis} onChange={this.handleEllipsisChange} />
</Form.Item>
<Form.Item label="Size">
<Radio.Group value={state.size} onChange={this.handleSizeChange}>
<Radio.Button value="default">Default</Radio.Button>
<Radio.Button value="middle">Middle</Radio.Button>
<Radio.Button value="small">Small</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item label="Table Scroll">
<Radio.Group value={xScroll} onChange={this.handleXScrollChange}>
<Radio.Button value={undefined}>Unset</Radio.Button>
<Radio.Button value="scroll">Scroll</Radio.Button>
<Radio.Button value="fixed">Fixed Columns</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item label="Table Layout">
<Radio.Group value={state.tableLayout} onChange={this.handleTableLayoutChange}>
<Radio.Button value={undefined}>Unset</Radio.Button>
<Radio.Button value="fixed">Fixed</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item label="Pagination Top">
<Radio.Group
value={this.state.top}
onChange={e => {
this.setState({ top: e.target.value });
}}
>
<Radio.Button value="topLeft">TopLeft</Radio.Button>
<Radio.Button value="topCenter">TopCenter</Radio.Button>
<Radio.Button value="topRight">TopRight</Radio.Button>
<Radio.Button value="none">None</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item label="Pagination Bottom">
<Radio.Group
value={this.state.bottom}
onChange={e => {
this.setState({ bottom: e.target.value });
}}
>
<Radio.Button value="bottomLeft">BottomLeft</Radio.Button>
<Radio.Button value="bottomCenter">BottomCenter</Radio.Button>
<Radio.Button value="bottomRight">BottomRight</Radio.Button>
<Radio.Button value="none">None</Radio.Button>
</Radio.Group>
</Form.Item>
</Form>
<Table
{...this.state}
pagination={{ position: [this.state.top, this.state.bottom] }}
columns={tableColumns}
dataSource={state.hasData ? data : null}
scroll={scroll}
/>
</>
);
}
}
ReactDOM.render(<Demo />, mountNode);
API#
Table#
Property | Description | Type | Default | Version |
---|---|---|---|---|
bordered | Whether to show all table borders | boolean | false | |
columns | Columns of table | ColumnsType[] | - | |
components | Override default table elements | TableComponents | - | |
dataSource | Data record array to be displayed | object[] | - | |
expandable | Config expandable content | expandable | - | |
footer | Table footer renderer | function(currentPageData) | - | |
getPopupContainer | The render container of dropdowns in table | (triggerNode) => HTMLElement | () => TableHtmlElement | |
loading | Loading status of table | boolean | Spin Props | false | |
locale | The i18n text including filter, sort, empty text, etc | object | Default Value | |
pagination | Config of pagination. You can ref table pagination config or full pagination document, hide it by setting it to false | object | - | |
rowClassName | Row's className | function(record, index): string | - | |
rowKey | Row's unique key, could be a string or function that returns a string | string | function(record): string | key | |
rowSelection | Row selection config | object | - | |
scroll | Whether the table can be scrollable, config | object | - | |
showHeader | Whether to show table header | boolean | true | |
showSorterTooltip | The header show next sorter direction tooltip. It will be set as the property of Tooltip if its type is object | boolean | Tooltip props | true | |
size | Size of table | default | middle | small | default | |
sortDirections | Supported sort way, could be ascend , descend | Array | [ascend , descend ] | |
sticky | Set sticky header and scroll bar | boolean | {offsetHeader?: number, offsetScroll?: number, getContainer?: () => HTMLElement} | - | 4.6.0 (getContainer: 4.7.0) |
summary | Summary content | (currentData) => ReactNode | - | |
tableLayout | The table-layout attribute of table element | - | auto | fixed | -fixed when header/columns are fixed, or using column.ellipsis | |
title | Table title renderer | function(currentPageData) | - | |
onChange | Callback executed when pagination, filters or sorter is changed | function(pagination, filters, sorter, extra: { currentDataSource: [], action: paginate | sort | filter }) | - | |
onHeaderRow | Set props on per header row | function(columns, index) | - | |
onRow | Set props on per row | function(record, index) | - |
onRow usage#
Same as onRow
onHeaderRow
onCell
onHeaderCell
<Table
onRow={(record, rowIndex) => {
return {
onClick: event => {}, // click row
onDoubleClick: event => {}, // double click row
onContextMenu: event => {}, // right button click row
onMouseEnter: event => {}, // mouse enter row
onMouseLeave: event => {}, // mouse leave row
};
}}
onHeaderRow={(columns, index) => {
return {
onClick: () => {}, // click header row
};
}}
/>
Column#
One of the Table columns
prop for describing the table's columns, Column has the same API.
Property | Description | Type | Default | Version |
---|---|---|---|---|
align | The specify which way that column is aligned | left | right | center | left | |
className | The className of this column | string | - | |
colSpan | Span of this column's title | number | - | |
dataIndex | Display field of the data record, support nest path by string array | string | string[] | - | |
defaultFilteredValue | Default filtered values | string[] | - | |
defaultSortOrder | Default order of sorted values | ascend | descend | - | |
editable | Whether column can be edited | boolean | false | |
ellipsis | The ellipsis cell content, not working with sorter and filters for now. tableLayout would be fixed when ellipsis is true or { showTitle?: boolean } | boolean | {showTitle?: boolean } | false | showTitle: 4.3.0 |
filterDropdown | Customized filter overlay | ReactNode | (props: FilterDropdownProps) => ReactNode | - | |
filterDropdownVisible | Whether filterDropdown is visible | boolean | - | |
filtered | Whether the dataSource is filtered | boolean | false | |
filteredValue | Controlled filtered value, filter icon will highlight | string[] | - | |
filterIcon | Customized filter icon | ReactNode | (filtered: boolean) => ReactNode | - | |
filterMultiple | Whether multiple filters can be selected | boolean | true | |
filterMode | To specify the filter interface | 'menu' | 'tree' | 'menu' | 4.17.0 |
filterSearch | Whether to be searchable for filter menu | Boolean | false | 4.17.0 |
filters | Filter menu config | object[] | - | |
fixed | (IE not support) Set column to be fixed: true (same as left) 'left' 'right' | boolean | string | false | |
key | Unique key of this column, you can ignore this prop if you've set a unique dataIndex | string | - | |
render | Renderer of the table cell. The return value should be a ReactNode | function(text, record, index) {} | - | |
responsive | The list of breakpoints at which to display this column. Always visible if not set. | Breakpoint[] | - | 4.2.0 |
shouldCellUpdate | Control cell render logic | (record, prevRecord) => boolean | - | 4.3.0 |
showSorterTooltip | If header show next sorter direction tooltip, override showSorterTooltip in table | boolean | Tooltip props | true | |
sortDirections | Supported sort way, override sortDirections in Table , could be ascend , descend | Array | [ascend , descend ] | |
sorter | Sort function for local sort, see Array.sort's compareFunction. If you need sort buttons only, set to true | function | boolean | - | |
sortOrder | Order of sorted values: 'ascend' 'descend' false | boolean | string | - | |
title | Title of this column | ReactNode | ({ sortOrder, sortColumn, filters }) => ReactNode | - | |
width | Width of this column (width not working?) | string | number | - | |
onCell | Set props on per cell | function(record, rowIndex) | - | |
onFilter | Function that determines if the row is displayed when filtered | function(value, record) => boolean | - | |
onFilterDropdownVisibleChange | Callback executed when filterDropdownVisible is changed | function(visible) {} | - | |
onHeaderCell | Set props on per header cell | function(column) | - |
ColumnGroup#
Property | Description | Type | Default |
---|---|---|---|
title | Title of the column group | ReactNode | - |
pagination#
Properties for pagination.
Property | Description | Type | Default |
---|---|---|---|
position | Specify the position of Pagination , could betopLeft | topCenter | topRight |bottomLeft | bottomCenter | bottomRight | Array | [bottomRight ] |
More about pagination, please check Pagination
.
expandable#
Properties for expandable.
Property | Description | Type | Default | Version |
---|---|---|---|---|
childrenColumnName | The column contains children to display | string | children | |
columnWidth | Set the width of the expand column | string | number | - | |
defaultExpandAllRows | Expand all rows initially | boolean | false | |
defaultExpandedRowKeys | Initial expanded row keys | string[] | - | |
expandedRowClassName | Expanded row's className | function(record, index, indent): string | - | |
expandedRowKeys | Current expanded row keys | string[] | - | |
expandedRowRender | Expanded container render for each row | function(record, index, indent, expanded): ReactNode | - | |
expandIcon | Customize row expand Icon. Ref example | function(props): ReactNode | - | |
expandRowByClick | Whether to expand row by clicking anywhere in the whole row | boolean | false | |
fixed | Whether the expansion icon is fixed. Optional true left right | boolean | string | false | 4.16.0 |
indentSize | Indent size in pixels of tree data | number | 15 | |
rowExpandable | Enable row can be expandable | (record) => boolean | - | |
showExpandColumn | Show expand column | boolean | true | 4.18.0 |
onExpand | Callback executed when the row expand icon is clicked | function(expanded, record) | - | |
onExpandedRowsChange | Callback executed when the expanded rows change | function(expandedRows) | - |
rowSelection#
Properties for row selection.
Property | Description | Type | Default | Version |
---|---|---|---|---|
checkStrictly | Check table row precisely; parent row and children rows are not associated | boolean | true | 4.4.0 |
columnTitle | Set the title of the selection column | ReactNode | - | |
columnWidth | Set the width of the selection column | string | number | 32px | |
fixed | Fixed selection column on the left | boolean | - | |
getCheckboxProps | Get Checkbox or Radio props | function(record) | - | |
hideSelectAll | Hide the selectAll checkbox and custom selection | boolean | false | 4.3.0 |
preserveSelectedRowKeys | Keep selection key even when it removed from dataSource | boolean | - | 4.4.0 |
renderCell | Renderer of the table cell. Same as render in column | function(checked, record, index, originNode) {} | - | 4.1.0 |
selectedRowKeys | Controlled selected row keys | string[] | number[] | [] | |
selections | Custom selection config, only displays default selections when set to true | object[] | boolean | - | |
type | checkbox or radio | checkbox | radio | checkbox | |
onChange | Callback executed when selected rows change | function(selectedRowKeys, selectedRows) | - | |
onSelect | Callback executed when select/deselect one row | function(record, selected, selectedRows, nativeEvent) | - | |
onSelectAll | Callback executed when select/deselect all rows | function(selected, selectedRows, changeRows) | - | |
onSelectInvert | Callback executed when row selection is inverted | function(selectedRowKeys) | - | |
onSelectNone | Callback executed when row selection is cleared | function() | - |
scroll#
Property | Description | Type | Default |
---|---|---|---|
scrollToFirstRowOnChange | Whether to scroll to the top of the table when paging, sorting, filtering changes | boolean | - |
x | Set horizontal scrolling, can also be used to specify the width of the scroll area, could be number, percent value, true and 'max-content' | string | number | true | - |
y | Set vertical scrolling, can also be used to specify the height of the scroll area, could be string or number | string | number | - |
selection#
Property | Description | Type | Default |
---|---|---|---|
key | Unique key of this selection | string | - |
text | Display text of this selection | ReactNode | - |
onSelect | Callback executed when this selection is clicked | function(changeableRowKeys) | - |
Using in TypeScript#
import { Table } from 'antd';
import { ColumnsType } from 'antd/es/table';
interface User {
key: number;
name: string;
}
const columns: ColumnsType<User> = [
{
key: 'name',
title: 'Name',
dataIndex: 'name',
},
];
const data: User[] = [
{
key: 0,
name: 'Jack',
},
];
export default () => (
<>
<Table<User> columns={columns} dataSource={data} />
/* JSX style usage */
<Table<User> dataSource={data}>
<Table.Column<User> key="name" title="Name" dataIndex="name" />
</Table>
</>
);
Here is the CodeSandbox for TypeScript.
Note#
According to the React documentation, every child in an array should be assigned a unique key. The values inside the Table's dataSource
and columns
should follow this rule. By default, dataSource[i].key
will be treated as the key value for dataSource
.
If dataSource[i].key
is not provided, then you should specify the primary key of dataSource value via rowKey
, as shown below. If not, warnings like the one above will show in browser console.
// primary key is uid
return <Table rowKey="uid" />;
// or
return <Table rowKey={record => record.uid} />;
Migrate to v4#
Table removes onRowClick
, onRowDoubleClick
, onRowMouseEnter
, onRowMouseLeave
and some other api which is already deprecated in v3. If you only use api listing in official document, that's OK.
Besides, the breaking change is changing dataIndex
from nest string path like user.age
to string array path like ['user', 'age']
. This help to resolve developer should additional work on the field which contains .
.
FAQ#
How to hide pagination when single page or no data?#
You can set hideOnSinglePage
with pagination
prop.
Table will return to first page when filter data.#
Table total page count usually reduce after filter data, we defaultly return to first page in case of current page is out of filtered results.
You may need to keep current page after filtering when fetch data from remote service, please check this demo as workaround.
Also you can use the action from extra param to determine when return to first page.
Why Table pagination show size changer?#
In order to improve user experience, Pagination show size changer by default when total > 50
since 4.1.0
. You can set showSizeChanger=false
to disable this feature.
Why Table fully render when state change?#
Table can not tell what state used in columns.render
, so it always need fully render to avoid sync issue. You can use column.shouldCellUpdate
to control render.
How to handle fixed column display over the mask layout?#
Fixed column use z-index
to make it over other columns. You will find sometime fixed columns also over your mask layout. You can set z-index
on your mask layout to resolve.