Table

A table allow to arrange large amounts of data into rows and columns.

Installyarn add @diallink-corp/convergo-react-table
Version4.1.2
Usageimport {Table} from '@diallink-corp/convergo-react-table'

Static Collection

The table component can either render static or dynamic collections of data. For simple table components that need to render a list of pre-defined data a static collection should be used. Static collections cannot be changed over time.

<Table aria-label='Users'>
  <TableHeader>
    <Column>First Name</Column>
    <Column>Last Name</Column>
    <Column>Age</Column>
  </TableHeader>
  <TableBody>
    <Row>
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row>
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row>
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row>
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table aria-label='Users'>
  <TableHeader>
    <Column>First Name</Column>
    <Column>Last Name</Column>
    <Column>Age</Column>
  </TableHeader>
  <TableBody>
    <Row>
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row>
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row>
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row>
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table aria-label="Users">
  <TableHeader>
    <Column>
      First Name
    </Column>
    <Column>
      Last Name
    </Column>
    <Column>
      Age
    </Column>
  </TableHeader>
  <TableBody>
    <Row>
      <Cell>
        Alexander
      </Cell>
      <Cell>
        Ehrenthal
      </Cell>
      <Cell>24</Cell>
    </Row>
    <Row>
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row>
      <Cell>Jeff</Cell>
      <Cell>
        Bezos
      </Cell>
      <Cell>57</Cell>
    </Row>
    <Row>
      <Cell>Tony</Cell>
      <Cell>
        Soprano
      </Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>

Dynamic Collection

For data that is non-static, such as data fetched from a backend, the table component supports dynamic collections. Dynamic collections support adding, updating and removing of the data that is being rendered.

function Example() {
  const columns = [
    {label: 'First Name', name: 'firstName'},
    {label: 'Last Name', name: 'lastName'},
    {label: 'Age', name: 'age'}
  ];

  const rows = [
    {id: 1, firstName: 'Alexander', lastName: 'Ehrenthal', age: 24},
    {id: 2, firstName: 'John', lastName: 'Doe', age: 34},
    {id: 3, firstName: 'Jeff', lastName: 'Bezos', age: 57},
    {id: 4, firstName: 'Tony', lastName: 'Soprano', age: 47}
  ];

  return (
    <Table aria-label='Users'>
      <TableHeader columns={columns}>
        {(column) => <Column key={column.name}>{column.label}</Column>}
      </TableHeader>
      <TableBody items={rows}>
        {(row) => <Row key={row.id}>{(key) => <Cell>{row[key]}</Cell>}</Row>}
      </TableBody>
    </Table>
  );
}
function Example() {
  const columns = [
    { label: 'First Name', name: 'firstName' },
    { label: 'Last Name', name: 'lastName' },
    { label: 'Age', name: 'age' }
  ];

  const rows = [
    {
      id: 1,
      firstName: 'Alexander',
      lastName: 'Ehrenthal',
      age: 24
    },
    { id: 2, firstName: 'John', lastName: 'Doe', age: 34 },
    {
      id: 3,
      firstName: 'Jeff',
      lastName: 'Bezos',
      age: 57
    },
    {
      id: 4,
      firstName: 'Tony',
      lastName: 'Soprano',
      age: 47
    }
  ];

  return (
    <Table aria-label="Users">
      <TableHeader columns={columns}>
        {(column) => (
          <Column key={column.name}>{column.label}</Column>
        )}
      </TableHeader>
      <TableBody items={rows}>
        {(row) => (
          <Row key={row.id}>
            {(key) => <Cell>{row[key]}</Cell>}
          </Row>
        )}
      </TableBody>
    </Table>
  );
}
function Example() {
  const columns = [
    {
      label:
        'First Name',
      name: 'firstName'
    },
    {
      label: 'Last Name',
      name: 'lastName'
    },
    {
      label: 'Age',
      name: 'age'
    }
  ];

  const rows = [
    {
      id: 1,
      firstName:
        'Alexander',
      lastName:
        'Ehrenthal',
      age: 24
    },
    {
      id: 2,
      firstName: 'John',
      lastName: 'Doe',
      age: 34
    },
    {
      id: 3,
      firstName: 'Jeff',
      lastName: 'Bezos',
      age: 57
    },
    {
      id: 4,
      firstName: 'Tony',
      lastName:
        'Soprano',
      age: 47
    }
  ];

  return (
    <Table aria-label="Users">
      <TableHeader
        columns={columns}
      >
        {(column) => (
          <Column
            key={column
              .name}
          >
            {column
              .label}
          </Column>
        )}
      </TableHeader>
      <TableBody
        items={rows}
      >
        {(row) => (
          <Row
            key={row.id}
          >
            {(key) => (
              <Cell>
                {row[
                  key
                ]}
              </Cell>
            )}
          </Row>
        )}
      </TableBody>
    </Table>
  );
}

Asynchronous Fetching

The table layout features a fixed header and a scrollable body in case the data is too large to fit into the viewport. The body will then become infinitely scrollable, unless a pagination element is added. The table component supports asynchronous fetching of data via the onLoadMoreItems prop. The table component uses its built-in infinite scrolling to achieve this. This means that when the user reaches the bottom of the table, the component will automatically load more data. If you provide an loadingState prop, the table component will show a spinner component, to indicate to the user, that more data is being fetched.

function Example() {
  const columns = [
    { name: 'Name', key: 'name' },
    { name: 'Height', key: 'height' },
    { name: 'Mass', key: 'mass' },
    { name: 'Birth Year', key: 'birth_year' }
  ];

  const list = useAsyncListState({
    async loadItems({ signal, cursor }) {
      if (cursor) {
        cursor = cursor.replace(/^http:\/\//i, 'https://');
      }

      const response = await fetch(
        cursor || `https://swapi.dev/api/people/?search=`,
        {
          signal
        }
      );
      const json = await response.json();

      return {
        items: json.results,
        cursor: json.next
      };
    }
  });

  return (
    <Table aria-label="Users" style={{ height: '200px' }}>
      <TableHeader columns={columns}>
        {(column) => <Column key={column.key}>{column.name}</Column>}
      </TableHeader>
      <TableBody
        items={list.items}
        loadingState={list.loadingState}
        onLoadMoreItems={list.loadMoreItems}
      >
        {(item) => (
          <Row key={item.name}>{(key) => <Cell>{item[key]}</Cell>}</Row>
        )}
      </TableBody>
    </Table>
  );
}
function Example() {
  const columns = [
    { name: 'Name', key: 'name' },
    { name: 'Height', key: 'height' },
    { name: 'Mass', key: 'mass' },
    { name: 'Birth Year', key: 'birth_year' }
  ];

  const list = useAsyncListState({
    async loadItems({ signal, cursor }) {
      if (cursor) {
        cursor = cursor.replace(/^http:\/\//i, 'https://');
      }

      const response = await fetch(
        cursor || `https://swapi.dev/api/people/?search=`,
        {
          signal
        }
      );
      const json = await response.json();

      return {
        items: json.results,
        cursor: json.next
      };
    }
  });

  return (
    <Table aria-label="Users" style={{ height: '200px' }}>
      <TableHeader columns={columns}>
        {(column) => (
          <Column key={column.key}>{column.name}</Column>
        )}
      </TableHeader>
      <TableBody
        items={list.items}
        loadingState={list.loadingState}
        onLoadMoreItems={list.loadMoreItems}
      >
        {(item) => (
          <Row key={item.name}>
            {(key) => <Cell>{item[key]}</Cell>}
          </Row>
        )}
      </TableBody>
    </Table>
  );
}
function Example() {
  const columns = [
    {
      name: 'Name',
      key: 'name'
    },
    {
      name: 'Height',
      key: 'height'
    },
    {
      name: 'Mass',
      key: 'mass'
    },
    {
      name: 'Birth Year',
      key: 'birth_year'
    }
  ];

  const list =
    useAsyncListState({
      async loadItems(
        {
          signal,
          cursor
        }
      ) {
        if (cursor) {
          cursor = cursor
            .replace(
              /^http:\/\//i,
              'https://'
            );
        }

        const response =
          await fetch(
            cursor ||
              `https://swapi.dev/api/people/?search=`,
            {
              signal
            }
          );
        const json =
          await response
            .json();

        return {
          items:
            json.results,
          cursor:
            json.next
        };
      }
    });

  return (
    <Table
      aria-label="Users"
      style={{
        height: '200px'
      }}
    >
      <TableHeader
        columns={columns}
      >
        {(column) => (
          <Column
            key={column
              .key}
          >
            {column.name}
          </Column>
        )}
      </TableHeader>
      <TableBody
        items={list
          .items}
        loadingState={list
          .loadingState}
        onLoadMoreItems={list
          .loadMoreItems}
      >
        {(item) => (
          <Row
            key={item
              .name}
          >
            {(key) => (
              <Cell>
                {item[
                  key
                ]}
              </Cell>
            )}
          </Row>
        )}
      </TableBody>
    </Table>
  );
}

Uncontrolled Selection

The table has selection support built-in. By passing in the selectionMode prop you can specify whether the table should be selectable. It will automatically add a new row with checkboxes to the very left. Through the defaultSelectedKeys prop you can handle uncontrolled selection.

<Table
  aria-label="Users"
  selectionMode="multiple"
  defaultSelectedKeys={['2', '3']}
>
  <TableHeader>
    <Column>First Name</Column>
    <Column>Last Name</Column>
    <Column>Age</Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table
  aria-label="Users"
  selectionMode="multiple"
  defaultSelectedKeys={['2', '3']}
>
  <TableHeader>
    <Column>First Name</Column>
    <Column>Last Name</Column>
    <Column>Age</Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table
  aria-label="Users"
  selectionMode="multiple"
  defaultSelectedKeys={[
    '2',
    '3'
  ]}
>
  <TableHeader>
    <Column>
      First Name
    </Column>
    <Column>
      Last Name
    </Column>
    <Column>
      Age
    </Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>
        Alexander
      </Cell>
      <Cell>
        Ehrenthal
      </Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>
        Bezos
      </Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>
        Soprano
      </Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>

Controlled Selection

You can also make the selection controlled through the selectedKeys and the onSelectionChange props.

function Example() {
  const [selectedKeys, setSelectedKeys] = useState(new Set(['2']));

  return (
    <Table
      aria-label="Users"
      selectionMode="multiple"
      selectedKeys={selectedKeys}
      onSelectionChange={setSelectedKeys}
    >
      <TableHeader>
        <Column>First Name</Column>
        <Column>Last Name</Column>
        <Column>Age</Column>
      </TableHeader>
      <TableBody>
        <Row key="1">
          <Cell>Alexander</Cell>
          <Cell>Ehrenthal</Cell>
          <Cell>24</Cell>
        </Row>
        <Row key="2">
          <Cell>John</Cell>
          <Cell>Doe</Cell>
          <Cell>34</Cell>
        </Row>
        <Row key="3">
          <Cell>Jeff</Cell>
          <Cell>Bezos</Cell>
          <Cell>57</Cell>
        </Row>
        <Row key="4">
          <Cell>Tony</Cell>
          <Cell>Soprano</Cell>
          <Cell>47</Cell>
        </Row>
      </TableBody>
    </Table>
  );
}
function Example() {
  const [selectedKeys, setSelectedKeys] = useState(
    new Set(['2'])
  );

  return (
    <Table
      aria-label="Users"
      selectionMode="multiple"
      selectedKeys={selectedKeys}
      onSelectionChange={setSelectedKeys}
    >
      <TableHeader>
        <Column>First Name</Column>
        <Column>Last Name</Column>
        <Column>Age</Column>
      </TableHeader>
      <TableBody>
        <Row key="1">
          <Cell>Alexander</Cell>
          <Cell>Ehrenthal</Cell>
          <Cell>24</Cell>
        </Row>
        <Row key="2">
          <Cell>John</Cell>
          <Cell>Doe</Cell>
          <Cell>34</Cell>
        </Row>
        <Row key="3">
          <Cell>Jeff</Cell>
          <Cell>Bezos</Cell>
          <Cell>57</Cell>
        </Row>
        <Row key="4">
          <Cell>Tony</Cell>
          <Cell>Soprano</Cell>
          <Cell>47</Cell>
        </Row>
      </TableBody>
    </Table>
  );
}
function Example() {
  const [
    selectedKeys,
    setSelectedKeys
  ] = useState(
    new Set(['2'])
  );

  return (
    <Table
      aria-label="Users"
      selectionMode="multiple"
      selectedKeys={selectedKeys}
      onSelectionChange={setSelectedKeys}
    >
      <TableHeader>
        <Column>
          First Name
        </Column>
        <Column>
          Last Name
        </Column>
        <Column>
          Age
        </Column>
      </TableHeader>
      <TableBody>
        <Row key="1">
          <Cell>
            Alexander
          </Cell>
          <Cell>
            Ehrenthal
          </Cell>
          <Cell>24</Cell>
        </Row>
        <Row key="2">
          <Cell>
            John
          </Cell>
          <Cell>
            Doe
          </Cell>
          <Cell>34</Cell>
        </Row>
        <Row key="3">
          <Cell>
            Jeff
          </Cell>
          <Cell>
            Bezos
          </Cell>
          <Cell>57</Cell>
        </Row>
        <Row key="4">
          <Cell>
            Tony
          </Cell>
          <Cell>
            Soprano
          </Cell>
          <Cell>47</Cell>
        </Row>
      </TableBody>
    </Table>
  );
}

Single Selection

If you want to only allow one row to be selected at a time, you can use the selectionMode prop to set it to single. It will then hide the select all checkbox in the top left corner and only allow you to select one row at a time.

<Table aria-label="Users" selectionMode="single" defaultSelectedKeys={['2']}>
  <TableHeader>
    <Column>First Name</Column>
    <Column>Last Name</Column>
    <Column>Age</Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table
  aria-label="Users"
  selectionMode="single"
  defaultSelectedKeys={['2']}
>
  <TableHeader>
    <Column>First Name</Column>
    <Column>Last Name</Column>
    <Column>Age</Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table
  aria-label="Users"
  selectionMode="single"
  defaultSelectedKeys={[
    '2'
  ]}
>
  <TableHeader>
    <Column>
      First Name
    </Column>
    <Column>
      Last Name
    </Column>
    <Column>
      Age
    </Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>
        Alexander
      </Cell>
      <Cell>
        Ehrenthal
      </Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>
        Bezos
      </Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>
        Soprano
      </Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>

Disallow Empty Selection

To disallow empty selection, you can use the disallowEmptySelection prop.

<Table
  aria-label="Users"
  selectionMode="multiple"
  defaultSelectedKeys={['2']}
  disallowEmptySelection
>
  <TableHeader>
    <Column>First Name</Column>
    <Column>Last Name</Column>
    <Column>Age</Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table
  aria-label="Users"
  selectionMode="multiple"
  defaultSelectedKeys={['2']}
  disallowEmptySelection
>
  <TableHeader>
    <Column>First Name</Column>
    <Column>Last Name</Column>
    <Column>Age</Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table
  aria-label="Users"
  selectionMode="multiple"
  defaultSelectedKeys={[
    '2'
  ]}
  disallowEmptySelection
>
  <TableHeader>
    <Column>
      First Name
    </Column>
    <Column>
      Last Name
    </Column>
    <Column>
      Age
    </Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>
        Alexander
      </Cell>
      <Cell>
        Ehrenthal
      </Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>
        Bezos
      </Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>
        Soprano
      </Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>

Disabled rows

You can disable certain rows from being selected by passing in the disabledKeys prop.

<Table aria-label="Users" selectionMode="multiple" disabledKeys={['2']}>
  <TableHeader>
    <Column>First Name</Column>
    <Column>Last Name</Column>
    <Column>Age</Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table
  aria-label="Users"
  selectionMode="multiple"
  disabledKeys={['2']}
>
  <TableHeader>
    <Column>First Name</Column>
    <Column>Last Name</Column>
    <Column>Age</Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table
  aria-label="Users"
  selectionMode="multiple"
  disabledKeys={['2']}
>
  <TableHeader>
    <Column>
      First Name
    </Column>
    <Column>
      Last Name
    </Column>
    <Column>
      Age
    </Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>
        Alexander
      </Cell>
      <Cell>
        Ehrenthal
      </Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>
        Bezos
      </Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>
        Soprano
      </Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>

Sorting

The table has built-in sorting through pressing the header. The columns that you would like to sort by can be passed the allowsSorting prop.

function Example() {
  const columns = [
    { name: 'Name', key: 'name' },
    { name: 'Height', key: 'height' },
    { name: 'Mass', key: 'mass' },
    { name: 'Birth Year', key: 'birth_year' }
  ];

  const list = useAsyncListState({
    async loadItems({ signal, cursor }) {
      if (cursor) {
        cursor = cursor.replace(/^http:\/\//i, 'https://');
      }

      const response = await fetch(
        cursor || `https://swapi.dev/api/people/?search=`,
        {
          signal
        }
      );
      const json = await response.json();

      return {
        items: json.results,
        cursor: json.next
      };
    },
    async sortItems({ items, sortDescriptor }) {
      return {
        items: items.sort((a, b) => {
          let first = a[sortDescriptor.column];
          let second = b[sortDescriptor.column];
          let cmp = (parseInt(first) || first) < (parseInt(second) || second)
            ? -1
            : 1;
          if (sortDescriptor.direction === 'descending') {
            cmp *= -1;
          }
          return cmp;
        })
      };
    }
  });

  return (
    <Table
      aria-label="Users"
      sortDescriptor={list.sortDescriptor}
      onSortChange={list.sortItems}
      style={{ height: '200px' }}
    >
      <TableHeader columns={columns}>
        {(column) => (
          <Column key={column.key} allowsSorting>{column.name}</Column>
        )}
      </TableHeader>
      <TableBody
        items={list.items}
        loadingState={list.loadingState}
        onLoadMoreItems={list.loadMoreItems}
      >
        {(item) => (
          <Row key={item.name}>{(key) => <Cell>{item[key]}</Cell>}</Row>
        )}
      </TableBody>
    </Table>
  );
}
function Example() {
  const columns = [
    { name: 'Name', key: 'name' },
    { name: 'Height', key: 'height' },
    { name: 'Mass', key: 'mass' },
    { name: 'Birth Year', key: 'birth_year' }
  ];

  const list = useAsyncListState({
    async loadItems({ signal, cursor }) {
      if (cursor) {
        cursor = cursor.replace(/^http:\/\//i, 'https://');
      }

      const response = await fetch(
        cursor || `https://swapi.dev/api/people/?search=`,
        {
          signal
        }
      );
      const json = await response.json();

      return {
        items: json.results,
        cursor: json.next
      };
    },
    async sortItems({ items, sortDescriptor }) {
      return {
        items: items.sort((a, b) => {
          let first = a[sortDescriptor.column];
          let second = b[sortDescriptor.column];
          let cmp =
            (parseInt(first) || first) <
                (parseInt(second) || second)
              ? -1
              : 1;
          if (sortDescriptor.direction === 'descending') {
            cmp *= -1;
          }
          return cmp;
        })
      };
    }
  });

  return (
    <Table
      aria-label="Users"
      sortDescriptor={list.sortDescriptor}
      onSortChange={list.sortItems}
      style={{ height: '200px' }}
    >
      <TableHeader columns={columns}>
        {(column) => (
          <Column key={column.key} allowsSorting>
            {column.name}
          </Column>
        )}
      </TableHeader>
      <TableBody
        items={list.items}
        loadingState={list.loadingState}
        onLoadMoreItems={list.loadMoreItems}
      >
        {(item) => (
          <Row key={item.name}>
            {(key) => <Cell>{item[key]}</Cell>}
          </Row>
        )}
      </TableBody>
    </Table>
  );
}
function Example() {
  const columns = [
    {
      name: 'Name',
      key: 'name'
    },
    {
      name: 'Height',
      key: 'height'
    },
    {
      name: 'Mass',
      key: 'mass'
    },
    {
      name: 'Birth Year',
      key: 'birth_year'
    }
  ];

  const list =
    useAsyncListState({
      async loadItems(
        {
          signal,
          cursor
        }
      ) {
        if (cursor) {
          cursor = cursor
            .replace(
              /^http:\/\//i,
              'https://'
            );
        }

        const response =
          await fetch(
            cursor ||
              `https://swapi.dev/api/people/?search=`,
            {
              signal
            }
          );
        const json =
          await response
            .json();

        return {
          items:
            json.results,
          cursor:
            json.next
        };
      },
      async sortItems(
        {
          items,
          sortDescriptor
        }
      ) {
        return {
          items: items
            .sort(
              (a, b) => {
                let first =
                  a[
                    sortDescriptor
                      .column
                  ];
                let second =
                  b[
                    sortDescriptor
                      .column
                  ];
                let cmp =
                  (parseInt(
                      first
                    ) ||
                      first) <
                      (parseInt(
                        second
                      ) ||
                        second)
                    ? -1
                    : 1;
                if (
                  sortDescriptor
                    .direction ===
                    'descending'
                ) {
                  cmp *=
                    -1;
                }
                return cmp;
              }
            )
        };
      }
    });

  return (
    <Table
      aria-label="Users"
      sortDescriptor={list
        .sortDescriptor}
      onSortChange={list
        .sortItems}
      style={{
        height: '200px'
      }}
    >
      <TableHeader
        columns={columns}
      >
        {(column) => (
          <Column
            key={column
              .key}
            allowsSorting
          >
            {column.name}
          </Column>
        )}
      </TableHeader>
      <TableBody
        items={list
          .items}
        loadingState={list
          .loadingState}
        onLoadMoreItems={list
          .loadMoreItems}
      >
        {(item) => (
          <Row
            key={item
              .name}
          >
            {(key) => (
              <Cell>
                {item[
                  key
                ]}
              </Cell>
            )}
          </Row>
        )}
      </TableBody>
    </Table>
  );
}

Column Alignment

You can easily change the alignment of the content in the columns through the align prop.

<Table aria-label='Users'>
  <TableHeader>
    <Column align='start'>First Name</Column>
    <Column align='center'>Last Name</Column>
    <Column align='end'>Age</Column>
  </TableHeader>
  <TableBody>
    <Row key='1'>
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key='2'>
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key='3'>
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key='4'>
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table aria-label='Users'>
  <TableHeader>
    <Column align='start'>First Name</Column>
    <Column align='center'>Last Name</Column>
    <Column align='end'>Age</Column>
  </TableHeader>
  <TableBody>
    <Row key='1'>
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key='2'>
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key='3'>
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key='4'>
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table aria-label="Users">
  <TableHeader>
    <Column align="start">
      First Name
    </Column>
    <Column align="center">
      Last Name
    </Column>
    <Column align="end">
      Age
    </Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>
        Alexander
      </Cell>
      <Cell>
        Ehrenthal
      </Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>
        Bezos
      </Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>
        Soprano
      </Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>

Column Widths

You can set the max of the column by setting the minWidth, width or maxWidth props.

<Table aria-label='Users'>
  <TableHeader>
    <Column minWidth={100}>First Name</Column>
    <Column width={120}>Last Name</Column>
    <Column maxWidth={300}>Age</Column>
  </TableHeader>
  <TableBody>
    <Row key='1'>
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key='2'>
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key='3'>
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key='4'>
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table aria-label='Users'>
  <TableHeader>
    <Column minWidth={100}>First Name</Column>
    <Column width={120}>Last Name</Column>
    <Column maxWidth={300}>Age</Column>
  </TableHeader>
  <TableBody>
    <Row key='1'>
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key='2'>
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key='3'>
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key='4'>
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table aria-label="Users">
  <TableHeader>
    <Column
      minWidth={100}
    >
      First Name
    </Column>
    <Column
      width={120}
    >
      Last Name
    </Column>
    <Column
      maxWidth={300}
    >
      Age
    </Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>
        Alexander
      </Cell>
      <Cell>
        Ehrenthal
      </Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>
        Bezos
      </Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>
        Soprano
      </Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>

Resizable Columns

To resize the table columns you can use the allowsResizing prop together with a defaultWidth prop.

<Table aria-label='Users'>
  <TableHeader>
    <Column allowsResizing defaultWidth="1fr">First Name</Column>
    <Column allowsResizing defaultWidth="2fr">Last Name</Column>
    <Column allowsResizing defaultWidth="1fr">Age</Column>
  </TableHeader>
  <TableBody>
    <Row key='1'>
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key='2'>
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key='3'>
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key='4'>
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table aria-label="Users">
  <TableHeader>
    <Column allowsResizing defaultWidth="1fr">
      First Name
    </Column>
    <Column allowsResizing defaultWidth="2fr">
      Last Name
    </Column>
    <Column allowsResizing defaultWidth="1fr">Age</Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table aria-label="Users">
  <TableHeader>
    <Column
      allowsResizing
      defaultWidth="1fr"
    >
      First Name
    </Column>
    <Column
      allowsResizing
      defaultWidth="2fr"
    >
      Last Name
    </Column>
    <Column
      allowsResizing
      defaultWidth="1fr"
    >
      Age
    </Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>
        Alexander
      </Cell>
      <Cell>
        Ehrenthal
      </Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>
        Bezos
      </Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>
        Soprano
      </Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>

Column Dividers

You can show dividers for each column through the showDivider prop.

<Table aria-label='Users'>
  <TableHeader>
    <Column align="start" showDivider>First Name</Column>
    <Column showDivider>Last Name</Column>
    <Column align="end" showDivider>Age</Column>
  </TableHeader>
  <TableBody>
    <Row key='1'>
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key='2'>
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key='3'>
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key='4'>
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table aria-label='Users'>
  <TableHeader>
    <Column align="start" showDivider>First Name</Column>
    <Column showDivider>Last Name</Column>
    <Column align="end" showDivider>Age</Column>
  </TableHeader>
  <TableBody>
    <Row key='1'>
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>24</Cell>
    </Row>
    <Row key='2'>
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key='3'>
      <Cell>Jeff</Cell>
      <Cell>Bezos</Cell>
      <Cell>57</Cell>
    </Row>
    <Row key='4'>
      <Cell>Tony</Cell>
      <Cell>Soprano</Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>
<Table aria-label="Users">
  <TableHeader>
    <Column
      align="start"
      showDivider
    >
      First Name
    </Column>
    <Column
      showDivider
    >
      Last Name
    </Column>
    <Column
      align="end"
      showDivider
    >
      Age
    </Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>
        Alexander
      </Cell>
      <Cell>
        Ehrenthal
      </Cell>
      <Cell>24</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>34</Cell>
    </Row>
    <Row key="3">
      <Cell>Jeff</Cell>
      <Cell>
        Bezos
      </Cell>
      <Cell>57</Cell>
    </Row>
    <Row key="4">
      <Cell>Tony</Cell>
      <Cell>
        Soprano
      </Cell>
      <Cell>47</Cell>
    </Row>
  </TableBody>
</Table>

Hide header

With the hideHeader prop you can hide the headers of certain columns. This is useful to hide the header for action columns. The label will then be rendered in a tooltip instead to compensate for the lack of a visual label.

<Table aria-label="Users">
  <TableHeader>
    <Column>First Name</Column>
    <Column>Last Name</Column>
    <Column hideHeader>Action</Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>
        <Button
          iconName="PlusIcon"
          iconVariant="outline"
          variant="text"
          size="small"
          aria-label="Add more information"
        />
      </Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>
        <Button
          iconName="PlusIcon"
          iconVariant="outline"
          variant="text"
          size="small"
          aria-label="Add more information"
        />
      </Cell>
    </Row>
  </TableBody>
</Table>
<Table aria-label="Users">
  <TableHeader>
    <Column>First Name</Column>
    <Column>Last Name</Column>
    <Column hideHeader>Action</Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
      <Cell>
        <Button
          iconName="PlusIcon"
          iconVariant="outline"
          variant="text"
          size="small"
          aria-label="Add more information"
        />
      </Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>
        <Button
          iconName="PlusIcon"
          iconVariant="outline"
          variant="text"
          size="small"
          aria-label="Add more information"
        />
      </Cell>
    </Row>
  </TableBody>
</Table>
<Table aria-label="Users">
  <TableHeader>
    <Column>
      First Name
    </Column>
    <Column>
      Last Name
    </Column>
    <Column hideHeader>
      Action
    </Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>
        Alexander
      </Cell>
      <Cell>
        Ehrenthal
      </Cell>
      <Cell>
        <Button
          iconName="PlusIcon"
          iconVariant="outline"
          variant="text"
          size="small"
          aria-label="Add more information"
        />
      </Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>Doe</Cell>
      <Cell>
        <Button
          iconName="PlusIcon"
          iconVariant="outline"
          variant="text"
          size="small"
          aria-label="Add more information"
        />
      </Cell>
    </Row>
  </TableBody>
</Table>

Overflow Mode

By default, the text of the table is truncated if it overflows. By setting the overflowMode prop to 'wrap', you can wrap the text instead.

<Table aria-label='Users' overflowMode='wrap'>
  <TableHeader>
    <Column>First Name</Column>
    <Column width={120}>Last Name</Column>
  </TableHeader>
  <TableBody>
    <Row key='1'>
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
    </Row>
    <Row key='2'>
      <Cell>John</Cell>
      <Cell>Some very very very long text as an example</Cell>
    </Row>
  </TableBody>
</Table>
<Table aria-label="Users" overflowMode="wrap">
  <TableHeader>
    <Column>First Name</Column>
    <Column width={120}>Last Name</Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>Alexander</Cell>
      <Cell>Ehrenthal</Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>
        Some very very very long text as an example
      </Cell>
    </Row>
  </TableBody>
</Table>
<Table
  aria-label="Users"
  overflowMode="wrap"
>
  <TableHeader>
    <Column>
      First Name
    </Column>
    <Column
      width={120}
    >
      Last Name
    </Column>
  </TableHeader>
  <TableBody>
    <Row key="1">
      <Cell>
        Alexander
      </Cell>
      <Cell>
        Ehrenthal
      </Cell>
    </Row>
    <Row key="2">
      <Cell>John</Cell>
      <Cell>
        Some very very
        very long text
        as an example
      </Cell>
    </Row>
  </TableBody>
</Table>

Empty State

To show the user that the table is empty when there is no data available you can use the renderEmptyState prop.

<Table
  aria-label="Users"
  renderEmptyState={() => (
    <IllustratedMessage>
      <Icon name="MagnifyingGlassIcon" variant="outline" />
      <Heading>No results found</Heading>
      <Text>
        No results could be found. Please create a new entity or modify your
        search.
      </Text>
    </IllustratedMessage>
  )}
>
  <TableHeader>
    <Column>First Name</Column>
    <Column>Last Name</Column>
  </TableHeader>
  <TableBody>
    {[]}
  </TableBody>
</Table>
<Table
  aria-label="Users"
  renderEmptyState={() => (
    <IllustratedMessage>
      <Icon
        name="MagnifyingGlassIcon"
        variant="outline"
      />
      <Heading>No results found</Heading>
      <Text>
        No results could be found. Please create a new
        entity or modify your search.
      </Text>
    </IllustratedMessage>
  )}
>
  <TableHeader>
    <Column>First Name</Column>
    <Column>Last Name</Column>
  </TableHeader>
  <TableBody>
    {[]}
  </TableBody>
</Table>
<Table
  aria-label="Users"
  renderEmptyState={() => (
    <IllustratedMessage>
      <Icon
        name="MagnifyingGlassIcon"
        variant="outline"
      />
      <Heading>
        No results
        found
      </Heading>
      <Text>
        No results
        could be found.
        Please create a
        new entity or
        modify your
        search.
      </Text>
    </IllustratedMessage>
  )}
>
  <TableHeader>
    <Column>
      First Name
    </Column>
    <Column>
      Last Name
    </Column>
  </TableHeader>
  <TableBody>
    {[]}
  </TableBody>
</Table>