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 tablewhich is recommended.