Import
import { Table } from '@contentful/f36-components';
import { Table } from '@contentful/f36-table';
Examples
Basic usage
function TableBasicUsageExample() {
return (
<Table>
<Table.Head>
<Table.Row>
<Table.Cell>Name</Table.Cell>
<Table.Cell>Email</Table.Cell>
<Table.Cell>Organization role</Table.Cell>
<Table.Cell>Last activity</Table.Cell>
</Table.Row>
</Table.Head>
<Table.Body>
<Table.Row>
<Table.Cell>Claus Mitchell</Table.Cell>
<Table.Cell>claus.mitchell@contentful.com</Table.Cell>
<Table.Cell>CEO</Table.Cell>
<Table.Cell>August 29, 2018</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>Johannes Ramos</Table.Cell>
<Table.Cell>johannes.ramos@contentful.com</Table.Cell>
<Table.Cell>CTO</Table.Cell>
<Table.Cell>July 27, 2019</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>Alex Kalinoski</Table.Cell>
<Table.Cell>alex.kalinoski@contentful.com</Table.Cell>
<Table.Cell>CDO</Table.Cell>
<Table.Cell>June 13, 2019</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
);
}
Dynamic creation
One very common use case for a table is that you will have a set of data and you would like to show a table row for each item in that set.
To achieve that result, you can iterate over the data and create Table.Row
and Table.Cell
for each item:
function TableDynamicCreation() {
const contentTypes = [
{
id: '1',
name: 'Category',
description:
'Categories can be applied to Courses and Lessons. Assigning Multiple categories is also possible.',
updatedAt: 'Nov 15, 2021',
status: 'published',
},
{
id: '2',
updatedAt: 'Nov 15, 2021',
status: 'draft',
},
{
id: '3',
name: 'Layout',
description:
'A page consisting of freely configurable and rearrangeable content modules.',
updatedAt: 'Nov 15, 2021',
status: 'published',
},
];
return (
<Table>
<Table.Head>
<Table.Row>
<Table.Cell>Name</Table.Cell>
<Table.Cell>Description</Table.Cell>
<Table.Cell>Updated</Table.Cell>
<Table.Cell>Status</Table.Cell>
</Table.Row>
</Table.Head>
<Table.Body>
{contentTypes.map((contentType) => {
return (
<Table.Row key={contentType.id}>
<Table.Cell>{contentType.name || 'Untitled'}</Table.Cell>
<Table.Cell>{contentType.description}</Table.Cell>
<Table.Cell>{contentType.updatedAt}</Table.Cell>
<Table.Cell>
<Badge
variant={
contentType.status === 'published' ? 'positive' : 'warning'
}
>
{contentType.status}
</Badge>
</Table.Cell>
</Table.Row>
);
})}
</Table.Body>
</Table>
);
}
With sorting
Table cells in the table header can be marked as sortable and sorted either in ascending or descending order.
function TableWithSorting() {
const contentTypes = [
{
id: '1',
name: 'Category',
description:
'Categories can be applied to Courses and Lessons. Assigning Multiple categories is also possible.',
updatedAt: 'Nov 1, 2021',
status: 'published',
},
{
id: '2',
updatedAt: 'Nov 11, 2021',
status: 'draft',
},
{
id: '3',
name: 'Layout',
description:
'A page consisting of freely configurable and rearrangeable content modules.',
updatedAt: 'Nov 18, 2021',
status: 'published',
},
];
const [sorting, setSorting] = useState(undefined);
const [sortedRows, setSortedRows] = useState(contentTypes);
const handleSort = ({ column }) => {
const direction =
sorting && sorting.column === column
? sorting.direction === TableCellSorting.Ascending
? TableCellSorting.Descending
: TableCellSorting.Ascending
: TableCellSorting.Ascending;
setSortedRows((rows) => {
const sorted = rows.sort((rowA, rowB) => {
let a = rowA[column];
const b = rowB[column];
if (column === 'name') {
a = rowA[column] || 'Untitled';
}
return a.localeCompare(b);
});
return direction === TableCellSorting.Ascending
? sorted
: sorted.reverse();
});
setSorting({ column, direction });
};
return (
<Table>
<Table.Head>
<Table.Row>
<Table.Cell
isSortable
onClick={() => handleSort({ column: 'name' })}
sortDirection={
sorting && sorting.column === 'name' ? sorting.direction : false
}
>
Name
</Table.Cell>
<Table.Cell>Description</Table.Cell>
<Table.Cell
isSortable
onClick={() => handleSort({ column: 'updatedAt' })}
sortDirection={
sorting && sorting.column === 'updatedAt'
? sorting.direction
: false
}
>
Updated
</Table.Cell>
<Table.Cell>Status</Table.Cell>
</Table.Row>
</Table.Head>
<Table.Body>
{sortedRows.map((contentType) => {
return (
<Table.Row key={contentType.id}>
<Table.Cell>{contentType.name || 'Untitled'}</Table.Cell>
<Table.Cell>{contentType.description}</Table.Cell>
<Table.Cell>{contentType.updatedAt}</Table.Cell>
<Table.Cell>
<Badge
variant={
contentType.status === 'published' ? 'positive' : 'warning'
}
>
{contentType.status}
</Badge>
</Table.Cell>
</Table.Row>
);
})}
</Table.Body>
</Table>
);
}
Table
Name | Type | Default |
---|
className | string CSS class to be appended to the root element | |
css | string number false true ComponentSelector Keyframes SerializedStyles ArrayInterpolation<undefined> ObjectInterpolation<undefined> (theme: any) => Interpolation<undefined> | |
layout | "inline" "embedded" | inline |
testId | string A [data-test-id] attribute used for testing purposes | cf-ui-table |
verticalAlign | "baseline" "bottom" "middle" "top" | top |
Table.Head
Name | Type | Default |
---|
children required | ReactNode | |
className | string CSS class to be appended to the root element | |
css | string number false true ComponentSelector Keyframes SerializedStyles ArrayInterpolation<undefined> ObjectInterpolation<undefined> (theme: any) => Interpolation<undefined> | |
isSticky | false true | false |
offsetTop | string number | |
testId | string A [data-test-id] attribute used for testing purposes | cf-ui-table-head |
Table.Body
Name | Type | Default |
---|
children required | ReactNode | |
className | string CSS class to be appended to the root element | |
css | string number false true ComponentSelector Keyframes SerializedStyles ArrayInterpolation<undefined> ObjectInterpolation<undefined> (theme: any) => Interpolation<undefined> | |
testId | string A [data-test-id] attribute used for testing purposes | |
Table.Row
Name | Type | Default |
---|
children required | ReactNode | |
className | string CSS class to be appended to the root element | |
css | string number false true ComponentSelector Keyframes SerializedStyles ArrayInterpolation<undefined> ObjectInterpolation<undefined> (theme: any) => Interpolation<undefined> | |
isSelected | false true | false |
testId | string A [data-test-id] attribute used for testing purposes | cf-ui-table-row |
Table.Cell
Name | Type | Default |
---|
align | "left" "center" "right" | |
as | HTML Tag or React Component (e.g. div, span, etc) | |
children | ReactNode | |
className | string CSS class to be appended to the root element | |
isSortable | false true | |
isTruncated | false true | |
isWordBreak | false true | |
sortDirection | "ascending" "descending" | |
testId | string A [data-test-id] attribute used for testing purposes | |
width | string number | |
Content guidelines
- Keep headers short
- Headers should be informative and descriptive
- Content in the table should be concise and scannable
Accessibility
- It will render tabular data using the native HTML element
table
which is recommended.