Table displays structured data with sortable columns and selectable rows.
Examples #
Import Syntax #
import Table from 'mineral-ui/Table';
Note
Unless otherwise declared, the examples below use the same data structure as shown in the basic example.
Basic Usage #
Pass Table an array of row objects, where each property key
will be used as a column header. It's also recommended to pass a rowKey
,
which is the row property key used to uniquely identify each row.
Fruits | Vegetables | Grains | Dairy | Protein |
---|---|---|---|---|
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmons | Fiddleheads | Quinoa | Milbenkase | Spirulina |
() => { const data = [ { Fruits: 'Pomello', Vegetables: 'Bok Choi', Grains: 'Chia', Dairy: 'Pule', Protein: 'Crickets' }, { Fruits: 'Starfruit', Vegetables: 'Romanesco', Grains: 'Sorghum', Dairy: 'Casu marzu', Protein: 'Barnacles' }, { Fruits: 'Durian', Vegetables: 'Ramps', Grains: 'Teff', Dairy: 'Vieux Lille', Protein: 'Inca nuts' }, { Fruits: 'Persimmons', Vegetables: 'Fiddleheads', Grains: 'Quinoa', Dairy: 'Milbenkase', Protein: 'Spirulina' } ]; return <Table data={data} rowKey="Fruits" title="Foods of the World" hideTitle />; }
Column Definition #
In addition to data
, you can pass an array of column
definition objects, detailed in the API.
Fresh Fruits | Veritable Vegetables | Good Grains | Delectable Dairy | Powerful Protein |
---|---|---|---|---|
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
() => { const columns = [ { content: 'Fresh Fruits', key: 'Fruits' }, { content: 'Veritable Vegetables', key: 'Vegetables' }, { content: 'Good Grains', key: 'Grains' }, { content: 'Delectable Dairy', key: 'Dairy' }, { content: 'Powerful Protein', key: 'Protein' } ]; return ( <Table columns={columns} data={data} rowKey="Fruits" title="Foods of the World" hideTitle /> ); }
Pagination #
Table can be used in conjunction with Pagination by passing sliced data to Table, calculated from the current page and page size.
Mineral | Color | Luster | Crystal System | Crystal Habit |
---|---|---|---|---|
Malachite | green | adamantine | monoclinic | botryoidal, stalactitic |
Fluorite | colorless | vitreous | isometric | coarse, granular |
Magnetite | black | metallic | isometric | octahedral |
() => { const pageSize = 3; class PaginatedTable extends Component { constructor(props) { super(props); this.state = { currentPage: 1, }; this.handlePageChange = this.handlePageChange.bind(this); } handlePageChange(currentPage) { this.setState({ currentPage }); } render () { const { currentPage } = this.state; const firstRow = (currentPage - 1) * pageSize; const lastRow = (currentPage - 1) * pageSize + pageSize; const slicedData = this.props.data.slice(firstRow, lastRow); return ( <DemoLayout> <Table data={slicedData} rowKey="name" columns={columns} title="Minerals" hideTitle /> <Pagination currentPage={currentPage} onPageChange={this.handlePageChange} pageSize={pageSize} totalCount={data.length} /> </DemoLayout> ); } } return <PaginatedTable data={data}/>; }
Sortable Columns #
Users can sort the rows in Table by column, enabled via the
sortable
prop. If the default sort comparator, below, is insufficient for
your needs, you can supply your own with the sortComparator
prop. You can
set the initially sorted column & direction with the defaultSort
prop. An
onSort
callback is also available. Note that the sortable
&
sortComparator
properties can be applied to Table via props or to individual
columns via column definition.
// Coerce null & undefined values to an empty string and normalize letter casing
const normalizedValue = (value) =>
value === null || value === undefined
? ''
: typeof value === 'string' ? value.toUpperCase() : value;
const defaultSortComparator: SortComparator = (a, b, key) => {
const valueA = normalizedValue(a[key]);
const valueB = normalizedValue(b[key]);
return valueA < valueB ? -1 : valueA > valueB ? 1 : 0;
};
This is available as a named export from the component:
import Table, { defaultSortComparator } from 'mineral-ui/Table';
In the example below, sorting is enabled for all columns except Protein, and the Dairy column sorts by length of the string rather than alphabetically.
Protein | ||||
---|---|---|---|---|
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
() => { const sortByLength = (a, b, key) => { const lengthA = a[key].length; const lengthB = b[key].length; return lengthA < lengthB ? -1 : lengthA > lengthB ? 1 : 0; }; const columns = [ { content: 'Fruits', key: 'Fruits' }, { content: 'Vegetables', key: 'Vegetables' }, { content: 'Grains', key: 'Grains' }, { content: 'Dairy', key: 'Dairy', sortComparator: sortByLength }, { content: 'Protein', key: 'Protein', sortable: false } ]; return ( <Table columns={columns} data={sharedData} rowKey="Fruits" defaultSort={{ key: 'Fruits' }} sortable title="Foods of the World" hideTitle /> ); }
Sortable Columns — Controlled #
Table controls its own sort state by default. It can optionally
be managed by the application as a controlled component via the control prop,
sort
. Two details of note:
defaultSortComparator
is available from Table.- Because a controlled sortable Table does no sorting on its own, it also does
not use any sortComparators defined in either props or column definitions.
The example below illustrates one way to define and use custom sort
comparator functions, with a
comparators
object.
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
() => { const evenRows = data.filter((row, index) => (index + 1)%2 === 0); const oddRows = data.filter((row, index) => (index + 1)%2 !== 0); const comparators = { // See the 'Sortable Columns' example for sortByLength implementation Dairy: sortByLength } const sortFn = (data, sort) => sort ? data.slice(0).sort((a, b) => { const comparator = comparators[sort.key] || defaultSortComparator; const asc = comparator(a, b, sort.key); const desc = asc * -1; return sort.descending ? desc : asc; }) : data; const initialState = { sort: { key: 'Fruits' } }; class MyTable extends Component { constructor(props) { super(props); this.state = initialState; this.handleSort = this.handleSort.bind(this); this.reset = this.reset.bind(this); this.sortDairyAscending = this.sortDairyAscending.bind(this); this.sortDairyDescending = this.sortDairyDescending.bind(this); this.sort = sortFn; } handleSort(sort) { this.setState({ sort }); } sortDairyAscending() { this.setState({ sort: { key: 'Dairy', descending: false } }); } sortDairyDescending() { this.setState({ sort: { key: 'Dairy', descending: true } }); } reset() { this.setState(initialState); } render() { const { data } = this.props; const { sort } = this.state; const sortedData = this.sort(data, sort); return ( <div> <Flex wrap> <FlexItem marginBottom="md"> <Button onClick={this.sortDairyAscending} size="medium">Sort Dairy ascending</Button> </FlexItem> <FlexItem marginBottom="md" marginRight="auto"> <Button onClick={this.sortDairyDescending} size="medium">Sort Dairy descending</Button> </FlexItem> <FlexItem marginBottom="md"> <Button onClick={this.reset} size="medium">Reset</Button> </FlexItem> </Flex> <Table data={sortedData} rowKey="Fruits" sortable sort={sort} onSort={this.handleSort} title="Foods of the World" hideTitle /> </div> ); } } return <MyTable data={data} />; }
Sortable Columns — Paginated #
Implement sorting for paginated data with Pagination by passing a controlled Table sliced data calculated from the active sort, current page, and page size.
Aragonite | white, var. | vitreous | orthorhombic | pseudohexagonal, columnar |
Azurite | azure | vitreous | monoclinic | prismatic, stalactitic |
Celestite | colorless, var. | vitreous | orthorhombic | fibrous, lamellar |
() => { class PaginatedTable extends Component { constructor(props) { super(props); this.state = { currentPage: 1, sort: { key: 'name' } }; this.handleSort = this.handleSort.bind(this); this.handlePageChange = this.handlePageChange.bind(this); this.sort = this.sort.bind(this); } handleSort(sort) { this.setState( { sort }, () => { this.handlePageChange(1) } ); } handlePageChange(currentPage) { this.setState({ currentPage }) } // See the 'Sortable Columns — Controlled' example for more implementation details sort(data, sort) { return sort ? data.slice(0).sort((a, b) => { const asc = defaultSortComparator(a, b, sort.key); const desc = asc * -1; return sort.descending ? desc : asc; }) : data; } render() { const { data } = this.props; const { currentPage, sort } = this.state; const pageSize = 3; const firstRow = (currentPage - 1) * pageSize; const lastRow = (currentPage - 1) * pageSize + pageSize; const slicedData = this.sort(data, sort).slice(firstRow, lastRow); return ( <DemoLayout> <Table sortable sort={sort} onSort={this.handleSort} data={slicedData} columns={columns} title="Minerals" hideTitle rowKey="Name" /> <Pagination currentPage={currentPage} onPageChange={this.handlePageChange} pageSize={pageSize} totalCount={data.length} /> </DemoLayout> ); } } return <PaginatedTable data={data} /> }
Selectable Rows #
Allow users to select rows with the selectable
prop.
Rows with a disabled
property set to true
will render a disabled
checkbox. You can set the initially selected rows with the
defaultSelectedRows
prop. onToggleRow
and onToggleAllRows
callbacks are also available.
Fruits | Vegetables | Grains | Dairy | Protein | |
---|---|---|---|---|---|
Pomello | Bok Choi | Chia | Pule | Crickets | |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles | |
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
() => { const data = [ sharedData[0], sharedData[1], { ...sharedData[2], disabled: true } ]; return ( <Table selectable defaultSelectedRows={[data[1]]} data={data} rowKey="Fruits" title="Foods of the World" hideTitle /> ); }
Selectable Rows — Controlled #
Table controls its own selected rows state by default. It can
optionally be managed by the application as a controlled component via the
control prop, selectedRows
.
Fruits | Vegetables | Grains | Dairy | Protein | |
---|---|---|---|---|---|
Pomello | Bok Choi | Chia | Pule | Crickets | |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles | |
Durian | Ramps | Teff | Vieux Lille | Inca nuts | |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
() => { const evenRows = data.filter((row, index) => (index + 1)%2 === 0); const oddRows = data.filter((row, index) => (index + 1)%2 !== 0); const initialState = { selected: [] }; class MyTable extends Component { constructor(props) { super(props); this.state = initialState; this.handleToggleRow = this.handleToggleRow.bind(this); this.handleToggleAllRows = this.handleToggleAllRows.bind(this); this.selectEvenRows = this.selectEvenRows.bind(this); this.selectOddRows = this.selectOddRows.bind(this); this.reset = this.reset.bind(this); } handleToggleRow(row) { this.setState((prevState) => { const selected = prevState.selected.slice(0); const index = selected.indexOf(row); const hasRow = index !== -1; hasRow ? selected.splice(index, 1) : selected.push(row); return { selected }; }); } handleToggleAllRows(rows) { this.setState({ selected: rows }); } selectEvenRows() { this.setState({ selected: evenRows }) } selectOddRows() { this.setState({ selected: oddRows }) } reset() { this.setState(initialState) } render() { return ( <div> <Flex wrap> <FlexItem marginBottom="md"> <Button onClick={this.selectEvenRows} size="medium">Select even rows</Button> </FlexItem> <FlexItem marginBottom="md" marginRight="auto"> <Button onClick={this.selectOddRows} size="medium">Select odd rows</Button> </FlexItem> <FlexItem marginBottom="md"> <Button onClick={this.reset} size="medium">Reset</Button> </FlexItem> </Flex> <Table data={data} rowKey="Fruits" selectable selectedRows={this.state.selected} onToggleRow={this.handleToggleRow} onToggleAllRows={this.handleToggleAllRows} title="Foods of the World" hideTitle /> </div> ); } } return <MyTable />; }
Selectable Rows — Paginated #
Implement selection for paginated data with Pagination by passing sliced selected rows calculated from the overall selected rows, current page, and page size.
Mineral | Color | Luster | Crystal System | Crystal Habit | |
---|---|---|---|---|---|
Malachite | green | adamantine | monoclinic | botryoidal, stalactitic | |
Fluorite | colorless | vitreous | isometric | coarse, granular | |
Magnetite | black | metallic | isometric | octahedral |
() => { class PaginatedTable extends Component { constructor(props) { super(props); this.state = { currentPage: 1, selected: [] }; this.handleToggleRow = this.handleToggleRow.bind(this); this.handleToggleAllRows = this.handleToggleAllRows.bind(this); this.pageSize = 3; this.handlePageChange = this.handlePageChange.bind(this); } handlePageChange(currentPage) { this.setState({ currentPage }); } handleToggleRow(row) { this.setState((prevState) => { const selected = prevState.selected.slice(0); const index = selected.indexOf(row); const hasRow = index !== -1; hasRow ? selected.splice(index, 1) : selected.push(row); return { selected }; }); } handleToggleAllRows(_, none) { const slicedData = this.getSlicedData(); this.setState(({ selected }) => ({ selected: none ? selected.concat(slicedData) : selected.filter((row) => slicedData.indexOf(row) === -1) })); } getSlicedData() { const { currentPage } = this.state; const firstRow = (currentPage - 1) * this.pageSize; const lastRow = (currentPage - 1) * this.pageSize + this.pageSize; return this.props.data.slice(firstRow, lastRow); } getSlicedSelected() { const { selected } = this.state; return this.getSlicedData().reduce((acc, row) => { if(selected.indexOf(row) !== -1) { acc.push(row); } return acc; }, []); } render () { const { currentPage } = this.state; return ( <DemoLayout> <Table selectable selectedRows={this.getSlicedSelected()} onToggleRow={this.handleToggleRow} onToggleAllRows={this.handleToggleAllRows} data={this.getSlicedData()} rowKey="name" columns={columns} title="Minerals" hideTitle /> <Pagination currentPage={currentPage} onPageChange={this.handlePageChange} pageSize={this.pageSize} totalCount={data.length} /> </DemoLayout> ); } } return <PaginatedTable data={data}/>; }
Title #
Display a title for your Table with the title
prop. You
can adjust the appearance (titleAppearance
) and the rendered HTML element
(titleAs
).
Use the hideTitle
prop to hide the title visually, while maintaining
accessibility.
Fruits | Vegetables | Grains | Dairy | Protein |
---|---|---|---|---|
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
Fruits | Vegetables | Grains | Dairy | Protein |
---|---|---|---|---|
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
<Grid alignItems="end" breakpoints={['57em']}> <GridItem span={[12, 6]} marginBottom={['lg', 0]}> <Table title="Foods of the World" data={data} rowKey="Fruits"/> </GridItem> <GridItem span={[12, 6]}> <Table title="Foods of the World" titleAppearance="h2" titleAs="h3" data={data} rowKey="Fruits"/> </GridItem> </Grid>
Primary Column #
It's recommended to identify a column as the primary column
(typically the first in the columns
array) with the primary
column
definition property. This will render cells in that column as
<th scope="row">
, which can provide helpful context to users of some
assistive technologies.
Fruit | Family | Etymology | Color | Taste |
---|---|---|---|---|
Pomello | Rutaceae | big citrus | lime | mild, sweet grapefruit |
Starfruit | Oxalidaceae | fruit of actions | dark yellow | sweet or sour |
Durian | Malvaceae | thorn | brown | unique |
Persimmon | Ebenaceae | divine fruit | red-orange | sweet |
() => { const data = [ { Fruit: 'Pomello', Etymology: 'big citrus', Family: 'Rutaceae', Color: 'lime', Taste: 'mild, sweet grapefruit' }, { Fruit: 'Starfruit', Etymology: 'fruit of actions', Family: 'Oxalidaceae', Color: 'dark yellow', Taste: 'sweet or sour' }, { Fruit: 'Durian', Etymology: 'thorn', Family: 'Malvaceae', Color: 'brown', Taste: 'unique' }, { Fruit: 'Persimmon', Etymology: 'divine fruit', Family: 'Ebenaceae', Color: 'red-orange', Taste: 'sweet' } ]; const columns = [ { content: 'Fruit', key: 'Fruit', primary: true }, { content: 'Family', key: 'Family' }, { content: 'Etymology', key: 'Etymology' }, { content: 'Color', key: 'Color' }, { content: 'Taste', key: 'Taste' } ]; return ( <Table columns={columns} data={data} title="Fruits" hideTitle /> ); }
Column Alignment #
Align the text of both the column header and the cells under it
with the textAlign
column definition property.
Fruits | Vegetables | Grains | Dairy | Protein |
---|---|---|---|---|
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
() => { const columns = [ { content: 'Fruits', key: 'Fruits' }, { content: 'Vegetables', key: 'Vegetables', textAlign: 'end' }, { content: 'Grains', key: 'Grains', textAlign: 'center' }, { content: 'Dairy', key: 'Dairy', textAlign: 'justify' }, { content: 'Protein', key: 'Protein' } ]; return ( <Table columns={columns} data={data} title="Foods of the World" hideTitle /> ); }
Density #
You can render Table with a more spacious appearance.
Fruits | Vegetables | Grains | Dairy | Protein |
---|---|---|---|---|
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
<Table
density="spacious"
data={data}
rowKey="Fruits"
title="Foods of the World"
hideTitle />
Striped Rows #
You can render Table with alternately-striped rows.
Fruits | Vegetables | Grains | Dairy | Protein |
---|---|---|---|---|
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
<Table
striped
data={data}
rowKey="Fruits"
title="Foods of the World"
hideTitle />
High Contrast #
You can render Table with a high contrast appearance.
Fruits | Vegetables | Grains | Dairy | Protein |
---|---|---|---|---|
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
<Table
highContrast
data={data}
rowKey="Fruits"
title="Foods of the World"
hideTitle />
Scrollable #
Table will allow horizontal scrolling by default when its
width is greater than that of its container. You can disable this behavior with
scrollable={false}
.
Fresh Fruits | Veritable Vegetables | Good Grains | Delectable Dairy | Powerful Protein |
---|---|---|---|---|
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
<Box width="50%"> <Table columns={columns} data={data} rowKey="Fruits" title="Foods of the World" hideTitle /> </Box>
Bidirectionality #
Table reverses its alignment when the direction
theme
variable is set to rtl
(right-to-left). You can use the messages
prop,
as in the example below, to set the various messages announced by assistive
technologies within the component.
خضروات | بقوليات | بروتين | ||
---|---|---|---|---|
Pomello | بوك تشوي | شيا | Pule | الصراصير |
فاكهة النجمة | Romanesco | الذرة | Casu marzu | النظارات |
دوريان | Ramps | التف | Vieux Lille | Inca nuts |
() => { const columns = [ { content: 'ثمار', key: 'Fruits', sortable: true }, { content: 'خضروات', key: 'Vegetables', textAlign: 'center' }, { content: 'بقوليات', key: 'Grains' }, { content: 'الألبان', key: 'Dairy', sortable: true, textAlign: 'end' }, { content: 'بروتين', key: 'Protein' } ]; const data = [ { Fruits: 'Pomello', Vegetables: 'بوك تشوي', Grains: 'شيا', Dairy: 'Pule', Protein: 'الصراصير' }, { Fruits: 'فاكهة النجمة', Vegetables: 'Romanesco', Grains: 'الذرة', Dairy: 'Casu marzu', Protein: 'النظارات' }, { Fruits: 'دوريان', Vegetables: 'Ramps', Grains: 'التف', Dairy: 'Vieux Lille', Protein: 'Inca nuts' } ]; const messages = { deselectAllRows: 'قم بإلغاء تحديد جميع الصفوف', deselectRow: 'إلغاء الصف', selectAllRows: 'حدد جميع الصفوف', selectedRows: 'الصفوف المختارة', selectRow: 'حدد الصف', sortColumnAscending: 'ترتيب العمود في تصاعدي الطلب', sortColumnDescending: 'ترتيب العمود في تنازلي الطلب' }; return ( <div dir="rtl"> <ThemeProvider theme={{ direction: 'rtl' }}> <Table columns={columns} data={data} rowKey="Fruits" title="الأطعمة اللذيذة" messages={messages} /> </ThemeProvider> </div> ) }
Custom Cell #
Use the cell
render prop in a column definiton to provide
custom rendering control of all cells in that column.
See our Render Props Guide for additional information, including important considerations and examples.
Fruits | Vegetables | Grains | Dairy | Protein |
---|---|---|---|---|
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
() => { const Root = styled('td')(({ theme }) => ({ padding: theme.space_stack_sm + ' ' + theme.space_inline_md, 'tr:hover > &': { backgroundColor: palette.green_10 } })); const Emoji = withProps({ 'aria-hidden': true, role: 'img' })(styled('span')( ({ theme }) => ({ display: 'inline-block', marginRight: theme.space_inline_sm }) )); const Content = styled('span')(({ theme }) => ({ fontSize: theme.fontSize_ui, textAlign: 'left' })); class CustomCell extends React.PureComponent { render() { return ( <Root {...this.props}> <Flex as="span"> <Emoji>🌿</Emoji> <Content>{this.props.children}</Content> </Flex> </Root> ); } } const cell = ({ props }) => <CustomCell {...props} />; const columns = [ { content: 'Fruits', key: 'Fruits' }, { content: 'Vegetables', key: 'Vegetables', cell }, { content: 'Grains', key: 'Grains' }, { content: 'Dairy', key: 'Dairy' }, { content: 'Protein', key: 'Protein' } ]; return ( <Table columns={columns} data={data} rowKey="Fruits" title="Foods of the World" hideTitle /> ); }
Custom Header Cell #
Use the headerCell
render prop in a column definiton to
provide custom rendering control of all table header cells in that column.
See our Render Props Guide for additional information, including important considerations and examples.
Refer to the custom sortable header cell if your data is sortable.
Fruits | Vegetables | Grains | Dairy | Protein |
---|---|---|---|---|
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
() => { const Root = styled('th')(({ theme }) => ({ padding: 0, verticalAlign: 'bottom', '&:not(:first-of-type)': { borderLeft: '1px dotted ' + theme.borderColor } })); const Inner = styled('span')(({theme}) => ({ alignItems: 'flex-end', display: 'flex', padding: pxToEm(12) + ' ' + theme.space_inline_md, whiteSpace: 'nowrap' })); const Content = styled('span')(({ theme }) => ({ fontSize: theme.fontSize_ui, fontWeight: theme.fontWeight_bold, textAlign: 'left' })); const Emoji = withProps({ 'aria-hidden': true, role: 'img' })(styled('span')(({ theme }) => ({ display: 'inline-block', marginRight: theme.space_inline_sm }) )); class CustomHeaderCell extends React.PureComponent { render() { const { children, emoji } = this.props; return ( <Root {...this.props}> <Inner> <Content> <Emoji>{emoji}</Emoji> {children} </Content> </Inner> </Root> ); } } const headerCell = ({ props }) => <CustomHeaderCell {...props} />; const columns = [ { content: 'Fruits', key: 'Fruits', emoji: '🍎', headerCell }, { content: 'Vegetables', key: 'Vegetables', emoji: '🥗', headerCell }, { content: 'Grains', key: 'Grains', emoji: '🌾', headerCell }, { content: 'Dairy', key: 'Dairy', emoji: '🥚', headerCell }, { content: 'Protein', key: 'Protein', emoji: '🍗', headerCell } ]; return ( <Table columns={columns} data={data} rowKey="Fruits" title="Delicious Foods" hideTitle /> ); }
Custom Sortable Header Cell #
Use the headerCell
render prop in a column definiton to
provide custom rendering control of all table header cells in that column.
See our Render Props Guide for additional information, including important considerations and examples.
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
() => { const ThemedSortableTableHeaderCell = themed(TableSortableHeaderCell)( ({ theme }) => ({ TableSortableHeaderCell_border_focus: '1px dotted ' + theme.color_black, TableSortableHeaderCell_color_focus: theme.color_black }) ); const StyledSortableTableHeaderCell = styled(ThemedSortableTableHeaderCell)( ({ direction, isSorted, theme }) => ({ boxShadow: isSorted ? direction === 'descending' ? 'inset 0 -3px ' + theme.color_black : 'inset 0 3px ' + theme.color_black : null, '& [role="img"]': { display: 'none' } }) ); class CustomSortableTableHeaderCell extends React.PureComponent { render() { const { props, state } = this.props; const styledSortableTableHeaderCellProps = { ...props, ...state }; return ( <StyledSortableTableHeaderCell {...styledSortableTableHeaderCellProps} /> ); } } const headerCell = (props) => <CustomSortableTableHeaderCell {...props} />; const columns = [ { content: 'Fruits', key: 'Fruits', headerCell }, { content: 'Vegetables', key: 'Vegetables', headerCell }, { content: 'Grains', key: 'Grains', headerCell }, { content: 'Dairy', key: 'Dairy', headerCell }, { content: 'Protein', key: 'Protein', headerCell } ]; return ( <Table data={data} columns={columns} sortable defaultSort={{ key: 'Fruits' }} rowKey="Fruits" title="Delicious Foods" hideTitle /> ); }
Custom Row #
Use the row
render prop as a row property in your data to
provide custom rendering control of the table row. See our Render Props Guide for additional information, including important considerations and examples.
Fruits | Vegetables | Grains | Dairy | Protein |
---|---|---|---|---|
Pomello | Bok Choi | Chia | Pule | Crickets |
Starfruit | Romanesco | Sorghum | Casu marzu | Barnacles |
Durian | Ramps | Teff | Vieux Lille | Inca nuts |
Persimmon | Fiddleheads | Quinoa | Milbenkase | Spirulina |
() => { const Root = styled('tr')(({ theme }) => ({ backgroundColor: theme.well_backgroundColor_warning })); const Cell = styled('td')(({ theme }) => ({ padding: theme.space_stack_sm + ' ' + theme.space_inline_md })); const Divider = styled('hr')(({ theme }) => ({ backgroundColor: theme.color_warning, border: 0, height: 1 })); class CustomRow extends React.PureComponent { render() { const { isSelectable } = this.props; const cellCount = Object.keys(data[0]).length + (isSelectable ? 1 : 0); return ( <Root {...this.props}> <Cell colSpan={cellCount}> <Divider /> </Cell> </Root> ); } } const row = ({ props }) => <CustomRow {...props} />; const data = [ sharedData[0], sharedData[1], { row }, sharedData[2], sharedData[3] ]; return ( <Table data={data} rowKey="Fruits" title="Foods of the World" hideTitle /> ); }
API & Theme #
Table Props #
The Table component takes the following React props.
Name | Type | Default | Description |
---|---|---|---|
columns | Array<Column> | Column definitions (see Column type for details) | |
data | Array<Row> | required | Row data (see example for more details) |
defaultSelectedRows | Array<Row> | Initially selected rows when | |
defaultSort | Initially sorted column & direction. Primarily for use with uncontrolled components. | ||
density | 'compact' | 'spacious' | 'compact' | Amount of vertical space in Table's cells |
hideHeader | boolean | Visually hide Table's header, but keep available for assistive technologies | |
hideTitle | boolean | Visually hide Table's title, but keep available for assistive technologies | |
highContrast | boolean | Render Table with high-contrast styles | |
messages | Various messages and labels used by Table (see example for more details) | ||
onSort | Called when data is sorted | ||
onToggleAllRows | Called when all rows are selected/deselected | ||
onToggleRow | Called when a single row is selected/deselected | ||
rowKey | string | Specifies a key in the row data that gives a row its unique identity. See the React docs. | |
scrollable | boolean | Determines the scrolling behavior when Table's width exceeds that of its container | |
selectable | boolean | Enable the user to select rows. Prepends a column for checkboxes to your Table. | |
selectedRows | Array<Row> | Selected rows when | |
sort | Sorted column & direction | ||
sortComparator | The sort comparator function used by sortable columns | ||
sortable | boolean | Enable the user to sort all columns | |
striped | boolean | Renders Table with alternating row stripes | |
title | React$Node | required | Title for Table |
titleAppearance | Available title styles (see Text) | ||
titleAs | 'h4' | Available title elements (see Text) |
Column Type #
Definition for each column in Table. See example.
Name | Type | Default | Description |
---|---|---|---|
cell | Provides custom rendering control. See the custom cell example and our render props guide. | ||
content | React$Node | required | Rendered content of the column header |
headerCell | Provides custom rendering control. See the custom header cell example and our render props guide. | ||
key | string | required | Used to look up the value for a column in your row data |
label | string | If a column's | |
maxWidth | number | string | Maximum width of the column. See | |
minWidth | number | string | Minimum width of the column. See | |
primary | boolean | Render cells in the column as | |
sortComparator | Define a custom comparator function for the column (see example) | ||
sortable | boolean | Enable user to sort the column (see example) | |
textAlign | 'start' | 'end' | 'center' | 'justify' | Align the text of both the column header and the cells (see example) | |
width | number | string | Width of the column. |
Table Theme Variables #
These variables can be used as hooks to override this component's
style at either a
local
or global
level. The theme
referenced below is whatever theme is available
from props to the instance of this component.
Variable | Value |
---|---|
Table_outline_focus | 1px solid borderColor_theme_focus |
TableCell_borderVertical | |
TableCell_borderVertical_highContrast | |
TableCell_fontSize | theme.fontSize_ui |
TableCell_paddingHorizontal | theme.space_inline_md |
TableCell_paddingVertical | theme.space_stack_sm |
TableCell_paddingVertical_spacious | 0.75em |
TableCell_verticalAlign | top |
TableHeaderCell_borderVertical | 1px dotted borderColor |
TableHeaderCell_borderVertical_highContrast | 1px dotted color_gray_80 |
TableHeaderCell_fontSize | theme.fontSize_ui |
TableHeaderCell_paddingHorizontal | theme.space_inline_md |
TableHeaderCell_paddingVertical | 0.75em |
TableHeaderCell_paddingVertical_spacious | theme.space_stack_md |
TableHeaderCell_verticalAlign | bottom |
TableHeaderCell_fontWeight | theme.fontWeight_bold |
TableHeader_borderBottom | 2px solid borderColor |
TableHeader_borderBottom_highContrast | 2px solid color_gray_80 |
TableHeader_borderTop | 1px solid borderColor |
TableHeader_borderTop_highContrast | 1px solid color_gray_80 |
TableRow_backgroundColor_highContrast_selected | theme.color_theme_20 |
TableRow_backgroundColor_highContrast_selectedHover | theme.color_theme_30 |
TableRow_backgroundColor_hover | theme.color_gray_20 |
TableRow_backgroundColor_selected | theme.color_theme_10 |
TableRow_backgroundColor_selectedHover | theme.color_theme_20 |
TableRow_backgroundColor_striped | theme.color_gray_10 |
TableRow_borderHorizontal | 1px solid color_white |
TableRow_borderHorizontal_highContrast | 1px solid color_gray_60 |
TableTitle_color | theme.h4_color |
TableTitle_fontSize | theme.h4_fontSize |
TableTitle_fontWeight | theme.h4_fontWeight |
TableTitle_marginBottom | theme.space_stack_sm |
Usage #
When/How to Use #
Table is best suited to data in which a user will need to compare data points or investigate relationships. For simpler data, consider a list structure; for more complex data or user needs, consider data visualization, possibly in addition to Table. Don't use Table for data sets with a blend of text, images, and data visualizations, or content with mixed formatting; use Card instead.
For Tables with many columns, striped rows can enhance readability. Tables that do not have enough columns to fill the width can be hard to read and should be displayed at a more appropriate width (perhaps using Box or other layout components).
Table is designed to optimize performance and minimize excess operations, but rendering more than 100 rows is still not recommended.
Best Practices #
If one or more columns in your table are sortable, make sure one of those columns has a default sort applied, so that its clear the table can be sorted.
Fruit Basket | $123.45 |
---|---|
Muffin Basket | $98.76 |
Avoid a mix of sortable and non-sortable columns. If one column is sortable, users will expect all columns to be sortable.
Price | |
---|---|
Fruit Basket | $123.45 |
Muffin Basket | $98.76 |
Designate a column as primary
in your column definitions,
unless your data has no column to serve that purpose.
Product | Price |
---|---|
Fruit Basket | $123.45 |
Muffin Basket | $98.76 |
Align numerical content (currency, numerical dates, etc...) to the end (right for LTR languages) for ease of comparison. If using decimals, numbers in a column should have a consistent precision.
Product | Price |
---|---|
Fruit Basket | $123.45 |
Muffin Basket | $98.76 |
Place the units for a column in the header cell, rather than repeating them in each data cell.
Name | Age (years) |
---|---|
Allison | 42 |
Sam | 65 |
Avoid displaying your own title for Table. Instead, use the
title*
prop(s) to display an accessibly-connected title with the desired
element and appearance.
Non-accessible Title
Product | Price |
---|---|
Fruit Basket | $123.45 |
Muffin Basket | $98.76 |