const items = [
    { id: 1, name: 'Chai', price: 18 },
    { id: 2, name: 'Chang', price: 19 },
    { id: 3, name: 'Aniseed Syrup', price: 10 },
    { id: 4, name: 'Chef Anton\'s Cajun Seasoning', price: 22 },
    { id: 5, name: 'Chef Anton\'s Gumbo Mix', price: 21.35 }
];

render (
    <Table data={items}>
        <TableColumn field="id" title="Id" width="60px" />
        <TableColumn field="name" title="Name" />
        <TableColumn field="price" title="Price" width="180px" format="{0:c}" />
    </Table>
);

Loading

Spinner could be shown in the case of loading data.

const items = [
    { id: 1, name: 'Chai', price: 18 },
    { id: 2, name: 'Chang', price: 19 },
    { id: 3, name: 'Aniseed Syrup', price: 10 },
    { id: 4, name: 'Chef Anton\'s Cajun Seasoning', price: 22 },
    { id: 5, name: 'Chef Anton\'s Gumbo Mix', price: 21.35 }
];

render (
    <Table data={items} loading>
        <TableColumn field="id" title="Id" width="60px" />
        <TableColumn field="name" title="Name" />
        <TableColumn field="price" title="Price" width="180px" format="{0:c}" />
    </Table>
);

No Stripes

Zebra stripes are useful when a Table is read-only or has simple content inside the cells. When content is more complex, the stripes can create too much noise in the UI.

const avatar = (test) => {
    return (
        <td>
        <Stack spacing={1} alignItems="center">
            <Avatar name={test.dataItem.name} size="s" autoColor />
            <BodyText size="small">{test.dataItem.name}</BodyText>
        </Stack>
        </td>
    );
};

const items = [
    { id: 1, name: 'Test', price: 18 },
    { id: 2, name: 'Chang', price: 19 },
    { id: 3, name: 'Aniseed Syrup', price: 10 },
    { id: 4, name: 'Chef Anton\'s Cajun Seasoning', price: 22 },
    { id: 5, name: 'Chef Anton\'s Gumbo Mix', price: 21.35 }
];

render (
    <Table data={items} striped={false}>
        <TableColumn field="id" title="Id" width="60px"  />
        <TableColumn field="name" title="Name" format="{0:c}" cell={avatar} />
        <TableColumn field="price" title="Price" width="180px" format="{0:c}" />
    </Table>
);

Borders

Borders can be configured as well, by default, vertical borders are disabled, and horizontal borders are enabled if the stripes are disabled.

const items = [
    { id: 1, name: 'Chai', price: 18 },
    { id: 2, name: 'Chang', price: 19 },
    { id: 3, name: 'Aniseed Syrup', price: 10 },
    { id: 4, name: 'Chef Anton\'s Cajun Seasoning', price: 22 },
    { id: 5, name: 'Chef Anton\'s Gumbo Mix', price: 21.35 }
];

render (
    <Table data={items} borders={{ vertical: true, horizontal: true }}>
        <TableColumn field="id" title="Id" width="60px" />
        <TableColumn field="name" title="Name" />
        <TableColumn field="price" title="Price" width="180px" format="{0:c}" />
    </Table>
);

Locked Columns

Individual columns can be configured to lock on scroll. This can be helpful when a lot of columns are present and important column information should be persistent on scroll.

const items = [
    { id: 1, name: 'Chai', price: 18, firstOrderedOn: new Date(1996, 8, 20), lastOrderedOn: new Date(1996, 8, 20) },
    { id: 2, name: 'Chang', price: 19, firstOrderedOn: new Date(1992, 5, 5), lastOrderedOn: new Date(2006, 1, 20) },
    { id: 3, name: 'Aniseed Syrup', price: 10, firstOrderedOn: new Date(2017, 11, 9), lastOrderedOn: new Date(2020, 4, 4) },
    { id: 4, name: 'Chef Anton\'s Cajun Seasoning', price: 22, firstOrderedOn: new Date(1876, 3, 27), lastOrderedOn: new Date(1899, 12, 31) },
    { id: 5, name: 'Chef Anton\'s Gumbo Mix', price: 21.35, firstOrderedOn: new Date(1970, 1, 31), lastOrderedOn: new Date(2016, 2, 29) }
];

render (
    <Table data={items} borders={{ vertical: true, horizontal: true }}>
        <TableColumn locked field="id" title="Id" width="60px" />
        <TableColumn locked field="name" title="Name" width="300px" />
        <TableColumn field="price" title="Price" format="{0:c}" width="100px" />
        <TableColumn field="firstOrderedOn" title="First Ordered On" format="{0:d}" width="200px" />
        <TableColumn field="lastOrderedOn" title="Last Ordered On" format="{0:d}" width="200px" />
    </Table>
);

Pagination

const Example = () => {
    const [pageState, setPageState] = React.useState({skip: 0,take: 3});

    const items = [
        { id: 1, name: 'Rose Milk Tea', price: 4.75 },
        { id: 2, name: 'Honey Milk Tea', price: 5.75 },
        { id: 3, name: 'Horchata Milk Tea', price: 5.00 },
        { id: 4, name: 'KitKat Milk Tea', price: 5.75 },
        { id: 6, name: 'Caramel Milk Tea', price: 4.75 },
        { id: 7, name: 'Original Milk Tea', price: 4.75 },
        { id: 8, name: 'Mango Milk Tea', price: 5.00 },
        { id: 9, name: 'Oolong Milk Tea', price: 4.75 }
    ];

    return(
        <Table
            data={process(items, pageState)}
            pageable
            total={items.length}
            skip={pageState.skip}
            take={pageState.take}
        >
            <TableColumn field="id" title="Id" width="60px" />
            <TableColumn field="name" title="Name" />
            <TableColumn field="price" title="Price" width="180px" format="{0:c}" />
        </Table>
    );
}
render (Example);

Resizable Columns

By default, columns are not resizable for an end user. Enabling feature can be useful when space is cramped and a user needs to make room for important columns.

const items = [
    { id: 1, name: 'Chai', price: 18 },
    { id: 2, name: 'Chang', price: 19 },
    { id: 3, name: 'Aniseed Syrup', price: 10 },
    { id: 4, name: 'Chef Anton\'s Cajun Seasoning', price: 22 },
    { id: 5, name: 'Chef Anton\'s Gumbo Mix', price: 21.35 }
];

render (
    <Table data={items} resizable borders={{ vertical: true, horizontal: true }}>
        <TableColumn field="id" title="Id" width="60px" />
        <TableColumn field="name" title="Name" />
        <TableColumn field="price" title="Price" width="180px" format="{0:c}" />
    </Table>
);

Selectable Columns

When a user needs to batch select many items, the Table can be configured to support row multi-selection.

const Example = () => {
    const sampleProducts = [
        {
            ProductID: 1,
            ProductName: 'Chai',
            FirstOrderedOn: new Date(1996, 8, 20),
        },
        {
            ProductID: 2,
            ProductName: 'Chang',
            FirstOrderedOn: new Date(1996, 7, 12),
        },
        {
            ProductID: 3,
            ProductName: 'Aniseed Syrup',
            FirstOrderedOn: new Date(1996, 8, 26),
        },
        {
            ProductID: 4,
            ProductName: 'Chef Anton’s Cajun Seasoning',
            FirstOrderedOn: new Date(1996, 9, 19),
        }
    ];

    const newData = sampleProducts.map((dataItem) => ({
        selected: false,
        ...dataItem,
    }));
    const [stateData, setStateData] = React.useState(newData);
    let lastSelectedIndex = 0;

    const selectionChange = (event) => {
        const data = stateData.map((item) => {
            if (item.ProductID === event.dataItem.ProductID) {
                item.selected = !event.dataItem.selected;
            }
            return item;
        });
        setStateData(data);
    };
    const rowClick = (event) => {
        let last = lastSelectedIndex;
        const data = [...stateData];
        const current = data.findIndex(
            (dataItem) => dataItem === event.dataItem
        );

        if (!event.nativeEvent.shiftKey) {
            lastSelectedIndex = current;
            last = current;
        }

        if (!event.nativeEvent.ctrlKey) {
            data.forEach((item) => (item.selected = false));
        }
        const select = !event.dataItem.selected;
        for (
            let i = Math.min(last, current);
            i <= Math.max(last, current);
            i += 1
        ) {
            data[i].selected = select;
        }
        setStateData(data);
    };

    const headerSelectionChange = (event) => {
        const checked = event.syntheticEvent.target.checked;
        const data = stateData.map((item) => {
            item.selected = checked;
            return item;
        });
        setStateData(data);
    };

    return (
        <Table
            data={stateData}
            total={sampleProducts.length}
            selectedField="selected"
            onSelectionChange={selectionChange}
            onHeaderSelectionChange={headerSelectionChange}
            onRowClick={rowClick}
        >
            <TableColumn
                field="selected"
                width="50px"
                headerSelectionValue={
                    stateData.findIndex((dataItem) => !dataItem.selected) === -1
                }
            />
            <TableColumn
                field="ProductID"
                title="Id"
                width="60px"
            />

            <TableColumn
                field="ProductName"
                title="Product Name"
            />

            <TableColumn
                field="FirstOrderedOn"
                title="First Ordered On"
                width="240px"
                format="{0:d}"
                filter="date"
            />

        </Table>
    );
}
render (Example);

Column Filters

Filters can be applied to individual columns of tables. They can be customized based on a few pre-built templates, such as numeric, date, and boolean.

const items = [
    { id: 1, name: 'Chai', price: 18, firstOrderedOn: new Date(1996, 8, 20) },
    { id: 2, name: 'Chang', price: 19, firstOrderedOn: new Date(1992, 5, 5)  },
    { id: 3, name: 'Aniseed Syrup', price: 10, firstOrderedOn: new Date(2017, 11, 9)  },
    { id: 4, name: 'Chef Anton\'s Cajun Seasoning', price: 22, firstOrderedOn: new Date(1876, 3, 27) },
    { id: 5, name: 'Chef Anton\'s Gumbo Mix', price: 21.35, firstOrderedOn: new Date(1970, 1, 31)  }
];

const ColumnMenu = (props) => (
    <TableColumnMenuFilter {...props} expanded />
);

render (
    <Table data={items} resizable borders={{ vertical: true, horizontal: true }}>
        <TableColumn field="id" title="Id" width="60px" />
        <TableColumn field="name" title="Name" columnMenu={ColumnMenu} />
        <TableColumn field="price" title="Price" width="180px" format="{0:c}" columnMenu={ColumnMenu} filter="numeric" />
        <TableColumn field="firstOrderedOn" title="First Ordered On" format="{0:d}" width="200px" columnMenu={ColumnMenu} filter="date" />
    </Table>
);

Best Practices

  • Content in cells should wrap instead of truncate, if there is enough room on the screen.
  • In general, content in cells should be left aligned.
  • Avoid displaying data that is not relevant to the Table's context.
  • Tabs can be used above a Table to filter clearly defined groups.

Related Components

  • For a simpler display of data with less functionality, use a Data List