# Table
> Table is a container for displaying information from a data set. It allows users to quickly scan, sort, compare, and take action on large amounts of data.

<Callout variant="tip">

For new tables, we recommend giving [GridTable](/components/tables/grid-table) a try. It has the same component structure but adds consistent column sizing and grouped headers support.

</Callout>

## Import

```js
import { Table } from '@gemini-suite/vera-react';
```

Table is a compound component that consists of multiple parts which can be composed together to achieve the desired table interface:

- `Table.Root`: The wrapper that contains all the parts of a table.
- `Table.Head`: The container that defines the head of the table's columns (equivalent of `<thead>` HTML element).
- `Table.Header`: A convenience container wrapping content into `Table.Head` with single `Table.Row` inside.
- `Table.ColumnHeader`: Renders the header cell of a column (equivalent of native `<th>` element). Wraps text content into `Table.TableColumnHeaderContent` and optionally shows `Table.SortIndicator`.
- `Table.ColumnHeaderContent`: Wrapper for column's header text contents.
- `Table.ColumnHeaderTitle`: Column's header title.
- `Table.ColumnHeaderSubtitle`: Column's header subtitle.
- `Table.ColumnResizeHandle`: The handle for resizing the column. Should be passed to `Table.ColumnHeader` via the `resizeHandle` prop.
- `Table.Body`: The container grouping a set of `Table.Row`s that are main content of the table (equivalent of native `<tbody>` element).
- `Table.Row`: Renders table row (equivalent of native `<tr>` element).
- `Table.Cell`: Cell to be placed in the `Table.Row` that displays single piece of data in the column (equivalent of native `<td>` element). Wraps text content into `Table.CellContent` automatically.
- `Table.CellContent`: Wrapper for cell's text contents.
- `Table.SelectionCell`: Composes `Table.Cell` and `Table.SelectionCellCheckbox`, to render a cell with checkbox for implementing selection of rows.
- `Table.SelectionCellCheckbox`: Customized [Checkbox](/components/forms/checkbox) component to be placed in the table cell.
- `Table.Foot`: The container grouping a set of `Table.Row`s which is placed at the bottom of the table (equivalent of native `<tfoot>` element).
- `Table.Footer`: A convenience container wrapping content into `Table.Foot` with single `Table.Row` inside.
- `Table.EmptyState`: The wrapper for providing additional context when the table data is either unavailable or nonexistent.

## Examples

### Basic

<Callout variant="tip">

If you need to display basic key and value pairs, consider [Data List](/components/data-list) instead.

</Callout>

```jsx
() => {
  const formatCurrency = value =>
    value.toLocaleString('en-GB', { style: 'currency', currency: 'EUR' });

  return (
    <Table.Root aria-label="Example table" highlightOnHover={false}>
      <Table.Header>
        <Table.ColumnHeader>{'Product ID'}</Table.ColumnHeader>
        <Table.ColumnHeader>{'Name'}</Table.ColumnHeader>
        <Table.ColumnHeader>{'Status'}</Table.ColumnHeader>
        <Table.ColumnHeader align="right" maxWidth={100}>
          {'Value'}
        </Table.ColumnHeader>
      </Table.Header>
      <Table.Body>
        <Table.Row>
          <Table.Cell>{'538902'}</Table.Cell>
          <Table.Cell>{'Golf club'}</Table.Cell>
          <Table.Cell>{'Payment pending'}</Table.Cell>
          <Table.Cell maxWidth={100} align="right">
            {formatCurrency(50)}
          </Table.Cell>
        </Table.Row>
        <Table.Row>
          <Table.Cell>{'570444'}</Table.Cell>
          <Table.Cell>{'Running shoes'}</Table.Cell>
          <Table.Cell>{'Completed'}</Table.Cell>
          <Table.Cell maxWidth={100} align="right">
            {formatCurrency(40)}
          </Table.Cell>
        </Table.Row>
        <Table.Row>
          <Table.Cell>{'100953'}</Table.Cell>
          <Table.Cell>{'Squash racquet'}</Table.Cell>
          <Table.Cell>{'Completed'}</Table.Cell>
          <Table.Cell maxWidth={100} align="right">
            {formatCurrency(30)}
          </Table.Cell>
        </Table.Row>
        <Table.Row>
          <Table.Cell>{'810245'}</Table.Cell>
          <Table.Cell>{'Football jersey'}</Table.Cell>
          <Table.Cell>{'Pending payment'}</Table.Cell>
          <Table.Cell maxWidth={100} align="right">
            {formatCurrency(35)}
          </Table.Cell>
        </Table.Row>
        <Table.Row>
          <Table.Cell>{'341002'}</Table.Cell>
          <Table.Cell>{'Volleyball net'}</Table.Cell>
          <Table.Cell>{'Completed'}</Table.Cell>
          <Table.Cell maxWidth={100} align="right">
            {formatCurrency(48)}
          </Table.Cell>
        </Table.Row>
      </Table.Body>
      <Table.Footer>
        <Table.Cell role="rowheader">
          <Table.CellContent>
            <Text fontWeight="semibold">{'Total value'}</Text>
          </Table.CellContent>
        </Table.Cell>
        <Table.Cell aria-colspan={3} align="right" maxWidth="100">
          <Table.CellContent>
            <Text fontWeight="semibold">{formatCurrency(203)}</Text>
          </Table.CellContent>
        </Table.Cell>
      </Table.Footer>
    </Table.Root>
  );
};
```

### Table appearance

The root `Table.Root` component accepts following properties that influence the table appearance:

- `cellSize` — controls the size of each cell.
- `alignY` — controls vertical alignment of content inside cells.
- `overflowStrategy` — wraps or truncate content inside a cell.
- `highlightOnHover` — controls whether rows within `Table.Body` should be highlighted on hover.
- `rowSeparation` - controls the appearance of row separation.
- `outlined` — controls whether the table should have an outer outline.

```jsx
() => {
  const data = [
    [
      'Torvald Halvor',
      'Shortbread gummies bear claw liquorice liquorice candy chupa chups cheesecake gingerbread.',
      'Oslo'
    ],
    [
      'Mikael Rikard',
      'Brownie icing jelly beans macaroon apple pie bear claw candy canes.',
      'Berlin'
    ],
    [
      'Ragnhild Tine',
      'Gummi bears lollipop biscuit apple pie pudding wafer. Sweet roll cheesecake toffee topping chupa chups. Lollipop dessert lemon drops croissant donut danish muffin.',
      'Oslo'
    ],
    [
      'Hanna Fredriksen',
      'Bonbon macaroon apple pie brownie dessert.',
      'Stockholm'
    ]
  ];

  const [cellSize, setCellSize] = React.useState('medium');
  const [align, setAlign] = React.useState('center');
  const [overflowStrategy, setOverflowStrategy] = React.useState('truncate');
  const [rowSeparation, setRowSeparation] = React.useState('dividers');
  const [isOutlined, setIsOutlined] = React.useState(true);

  return (
    <Flex flow="column">
      <Flex
        gap={{
          '@initial': 'spacingL',
          '@mqLargeAndUp': 'spacingXxl'
        }}
        wrap="wrap"
        cross="start"
      >
        <FormField.Root as={Flex} flow="column">
          <FormField.Label as="span">{'Size'}</FormField.Label>
          <RadioGroup
            defaultValue={'medium'}
            onChange={value => setCellSize(value)}
          >
            <Flex flow="column" gap="spacingXs">
              <Radio.Root value="small">
                <Radio.Indicator />
                <Radio.Label>{'Small'}</Radio.Label>
              </Radio.Root>
              <Radio.Root value="medium">
                <Radio.Indicator />
                <Radio.Label>{'Medium'}</Radio.Label>
              </Radio.Root>
            </Flex>
          </RadioGroup>
        </FormField.Root>
        <FormField.Root as={Flex} flow="column">
          <FormField.Label as="span">{'Align'}</FormField.Label>
          <RadioGroup
            defaultValue={'center'}
            onChange={value => setAlign(value)}
          >
            <Flex flow="column" gap="spacingXs">
              <Radio.Root value="top">
                <Radio.Indicator />
                <Radio.Label>{'Top'}</Radio.Label>
              </Radio.Root>
              <Radio.Root value="center">
                <Radio.Indicator />
                <Radio.Label>{'Center'}</Radio.Label>
              </Radio.Root>
              <Radio.Root value="bottom">
                <Radio.Indicator />
                <Radio.Label>{'Bottom'}</Radio.Label>
              </Radio.Root>
            </Flex>
          </RadioGroup>
        </FormField.Root>
        <FormField.Root as={Flex} flow="column">
          <FormField.Label as="span">{'Overflow strategy'}</FormField.Label>
          <RadioGroup
            defaultValue={'truncate'}
            onChange={value => setOverflowStrategy(value)}
          >
            <Flex flow="column" gap="spacingXs">
              <Radio.Root value="truncate">
                <Radio.Indicator />
                <Radio.Label>{'Truncate'}</Radio.Label>
              </Radio.Root>
              <Radio.Root value="wrap">
                <Radio.Indicator />
                <Radio.Label>{'Wrap'}</Radio.Label>
              </Radio.Root>
            </Flex>
          </RadioGroup>
        </FormField.Root>
        <FormField.Root as={Flex} flow="column">
          <FormField.Label as="span">{'Row separation'}</FormField.Label>
          <RadioGroup
            defaultValue={'dividers'}
            onChange={value => setRowSeparation(value)}
          >
            <Flex flow="column" gap="spacingXs">
              <Radio.Root value="dividers">
                <Radio.Indicator />
                <Radio.Label>{'Dividers'}</Radio.Label>
              </Radio.Root>
              <Radio.Root value="stripes">
                <Radio.Indicator />
                <Radio.Label>{'Stripes'}</Radio.Label>
              </Radio.Root>
            </Flex>
          </RadioGroup>
        </FormField.Root>
        <FormField.Root as={Flex} flow="column">
          <FormField.Label as="span">{'Outlined'}</FormField.Label>
          <Checkbox.Root
            isChecked={isOutlined}
            onChange={event => setIsOutlined(event.target.checked)}
          >
            <Checkbox.Indicator />
            <Checkbox.Label>{'Enable'}</Checkbox.Label>
          </Checkbox.Root>
        </FormField.Root>
      </Flex>

      <Table.Root
        aria-label="Table appearance props example"
        cellSize={cellSize}
        alignY={align}
        overflowStrategy={overflowStrategy}
        rowSeparation={rowSeparation}
        outlined={isOutlined}
      >
        <Table.Header>
          <Table.ColumnHeader width={160}>{'Name'}</Table.ColumnHeader>
          <Table.ColumnHeader minWidth={320}>{'Comment'}</Table.ColumnHeader>
          <Table.ColumnHeader width={160}>{'Office'}</Table.ColumnHeader>
        </Table.Header>
        <Table.Body>
          {data.map((row, rowIndex) => (
            <Table.Row key={rowIndex}>
              {row.map((cell, cellIndex) => (
                <Table.Cell
                  width={cellIndex === 1 ? undefined : 160}
                  minWidth={cellIndex === 1 ? 320 : undefined}
                  key={cellIndex}
                >
                  {cell}
                </Table.Cell>
              ))}
            </Table.Row>
          ))}
        </Table.Body>
      </Table.Root>
    </Flex>
  );
};
```

### Cell appearance

The `Table.Cell` component accepts following props to adjust individual cell appearance:

- `align` — controls the horizontal alignment of cell contents.
- `maxWidth` — sets the maximum width of the cell.
- `minWidth` — sets the minimum width of the cell.
- `width` — sets the width of the cell.

<Callout variant="tip">

Since the table layout is created with [CSS Flexbox](https://www.w3.org/TR/css-flexbox-1/), all cells have evenly distributed widths by default.

</Callout>

```jsx
<Table.Root aria-label="Cell appearance props example">
  <Table.Header>
    <Table.ColumnHeader showDivider minWidth={140} maxWidth={220}>
      {'Name'}
    </Table.ColumnHeader>
    <Table.ColumnHeader minWidth={320}>{'Comment'}</Table.ColumnHeader>
    <Table.ColumnHeader align="right" width={120}>
      {'Created'}
    </Table.ColumnHeader>
  </Table.Header>
  <Table.Body>
    <Table.Row>
      <Table.Cell showDivider minWidth={140} maxWidth={220}>
        Hanna Fredriksen
      </Table.Cell>
      <Table.Cell minWidth={320}>
        Macaroon biscuit halvah gummi bears tootsie roll oat cake cupcake.
      </Table.Cell>
      <Table.Cell align="right" width={120}>
        <Table.CellContent as="time">2022-10-15</Table.CellContent>
      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell showDivider minWidth={140} maxWidth={220}>
        Agnes Hagen
      </Table.Cell>
      <Table.Cell minWidth={320}>
        Marshmallow shortbread croissant biscuit sesame snaps bear claw dragée
        pudding cupcake.
      </Table.Cell>
      <Table.Cell align="right" width={120}>
        <Table.CellContent as="time">2022-11-23</Table.CellContent>
      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell showDivider minWidth={140} maxWidth={220}>
        Markus Gundersen
      </Table.Cell>
      <Table.Cell minWidth={320}>
        Bear claw pastry tiramisu gummi bears gingerbread.
      </Table.Cell>
      <Table.Cell align="right" width={120}>
        <Table.CellContent as="time">2022-12-05</Table.CellContent>
      </Table.Cell>
    </Table.Row>
  </Table.Body>
</Table.Root>
```

### Rows selection

Use `Table.SelectionCell` to implement selection of rows.

```jsx
() => {
  const data = [
    ['Error while fetching data', '500', 'Job_a35b34'],
    [
      'Response status code does not indicate success: Internal Server Error',
      '500',
      'Job_b3dffs'
    ],
    ['User not authenticated', '403', 'Job_98dz93'],
    ['Method not allowed', '405', 'Job_dl10s1'],
    ['Error while fetching data', '500', 'Job_a902k3'],
    ['Request timeout', '408', 'Job_a35b34']
  ];
  const [selectedRows, setSelectedRows] = React.useState([]);

  const handleSelectRow = rowIndex => {
    if (selectedRows.includes(rowIndex)) {
      setSelectedRows(selectedRows.filter(row => row !== rowIndex));
    } else {
      setSelectedRows([...selectedRows, rowIndex]);
    }
  };

  const handleSelectAllRows = () => {
    setSelectedRows(
      selectedRows.length ? [] : data.map((_val, index) => index)
    );
  };

  return (
    <Flex flow="column">
      <Box>
        {'Selected rows: '}
        {selectedRows.length}
      </Box>
      <Table.Root aria-label="Table with selection">
        <Table.Header>
          <Table.ColumnHeader maxWidth={40}>
            <Table.SelectionCellCheckbox
              onChange={handleSelectAllRows}
              isChecked={selectedRows.length === data.length}
              isIndeterminate={
                selectedRows.length && selectedRows.length < data.length
              }
              aria-label="Select all rows"
            />
          </Table.ColumnHeader>
          <Table.ColumnHeader minWidth="50%">{'Message'}</Table.ColumnHeader>
          <Table.ColumnHeader>{'Status code'}</Table.ColumnHeader>
          <Table.ColumnHeader>{'Job name'}</Table.ColumnHeader>
        </Table.Header>
        <Table.Body>
          {data.map((row, rowIndex) => (
            <Table.Row
              key={rowIndex}
              aria-selected={selectedRows.includes(rowIndex)}
            >
              <Table.SelectionCell
                aria-label="Select row"
                isChecked={selectedRows.includes(rowIndex)}
                onChange={() => handleSelectRow(rowIndex)}
              />
              {row.map((cell, cellIndex) => (
                <Table.Cell
                  minWidth={cellIndex === 0 && '50%'}
                  title={cell}
                  key={cellIndex}
                >
                  {cell}
                </Table.Cell>
              ))}
            </Table.Row>
          ))}
        </Table.Body>
      </Table.Root>
    </Flex>
  );
};
```

### Table overflow

When there isn’t enough space to fit all the rows or columns within table's container, it becomes scrollable.

You can apply sticky positioning to `Table.Header` and `Table.Footer` along the vertical axis via the `stickyOffset` property:

- the header will be offset against the top edge.
- the footer will be offset against the bottom edge.

`Table.ColumnHeader` and `Table.Cell` can be "sticky" along the horizontal axis:

- positive values (e.g. `0`) will offset the cell against the left edge.
- negative values (e.g. `-0`) will offset the cell against the right edge.

<Callout variant="tip">

When `stickyOffset` is provided, the `showDivider` property will default to `true`.

</Callout>

```jsx
() => {
  const data = React.useMemo(
    () => [
      {
        id: 1,
        name: 'Torvald Halvor',
        title: 'Finance manager',
        age: 56,
        income: 4051,
        tax: 891.22
      },
      {
        id: 2,
        name: 'Mikael Rikard',
        title: 'QA Engineer',
        age: 42,
        income: 3522,
        tax: 774.84
      },
      {
        id: 3,
        name: 'Ragnhild Tine',
        title: 'Software tester',
        age: 29,
        income: 3262,
        tax: 717.64
      },
      {
        id: 4,
        name: 'Liv Margareta',
        title: 'Software developer',
        age: 35,
        income: 4051,
        tax: 891.22
      },
      {
        id: 5,
        name: 'Johnny Nowak',
        title: 'Intern',
        age: 24,
        income: 1479,
        tax: 325.38
      },
      {
        id: 6,
        name: 'Fritjof Snorre',
        title: 'Technical writer',
        age: 47,
        income: 2305,
        tax: 507.1
      },
      {
        id: 7,
        name: 'Angelica Ramos',
        title: 'UX/UI Designer',
        age: 30,
        income: 3599,
        tax: 791.78
      },
      {
        id: 8,
        name: 'Snorre Lauritz',
        title: 'Senior software developer',
        age: 52,
        income: 6009,
        tax: 1321.98
      },
      {
        id: 9,
        name: 'Finn Jo',
        title: 'HR manager',
        age: 45,
        income: 2500,
        tax: 550
      },
      {
        id: 10,
        name: 'Inge Arnt',
        title: 'Software developer',
        age: 31,
        income: 4000,
        tax: 880
      },
      {
        id: 11,
        name: 'Ulf Sindre',
        title: 'UX/UI Designer',
        age: 25,
        income: 3805,
        tax: 837.1
      },
      {
        id: 12,
        name: 'Bradley Greer',
        title: 'Chief Executive Officer',
        age: 52,
        income: 8891,
        tax: 2198.02
      },
      {
        id: 13,
        name: 'Caesar Vance',
        title: 'Intern',
        age: 21,
        income: 1699,
        tax: 373.78
      }
    ],
    []
  );

  const total = React.useMemo(() => {
    return data.reduce(
      (acc, item) => ({
        income: acc.income + item.income,
        tax: acc.tax + item.tax
      }),
      { income: 0, tax: 0 }
    );
  }, [data]);

  const formatCurrency = value =>
    value.toLocaleString('en-GB', { style: 'currency', currency: 'EUR' });

  return (
    <Box css={{ height: 300 }}>
      <Table.Root aria-label="Table overflow example">
        <Table.Header stickyOffset={0}>
          <Table.ColumnHeader width={50}>{'ID'}</Table.ColumnHeader>
          <Table.ColumnHeader width={160} stickyOffset={0}>
            {'Name'}
          </Table.ColumnHeader>
          <Table.ColumnHeader minWidth={180}>
            {'Job position'}
          </Table.ColumnHeader>
          <Table.ColumnHeader width={120} align="right">
            {'Age'}
          </Table.ColumnHeader>
          <Table.ColumnHeader width={160} align="right">
            {'Income'}
          </Table.ColumnHeader>
          <Table.ColumnHeader width={160} align="right">
            {'Tax'}
          </Table.ColumnHeader>
          <Table.ColumnHeader width={160} align="right" stickyOffset={-0}>
            {'Net pay'}
          </Table.ColumnHeader>
        </Table.Header>
        <Table.Body>
          {data.map(row => (
            <Table.Row key={row.id}>
              <Table.Cell width={50}>{row.id}</Table.Cell>
              <Table.Cell width={160} stickyOffset={0}>
                {row.name}
              </Table.Cell>
              <Table.Cell minWidth={180}>{row.title}</Table.Cell>
              <Table.Cell width={120} align="right">
                {row.age}
              </Table.Cell>
              <Table.Cell width={160} align="right">
                {formatCurrency(row.income)}
              </Table.Cell>
              <Table.Cell width={160} align="right">
                {formatCurrency(row.tax)}
              </Table.Cell>
              <Table.Cell width={160} align="right" stickyOffset={-0}>
                {formatCurrency(row.income - row.tax)}
              </Table.Cell>
            </Table.Row>
          ))}
        </Table.Body>
        <Table.Footer stickyOffset={0}>
          <Table.Cell width={50}>
            <VisuallyHidden>N/A</VisuallyHidden>
          </Table.Cell>
          <Table.Cell role="rowheader" width={160} stickyOffset={0}>
            <Table.CellContent fontWeight="semibold">Totals</Table.CellContent>
          </Table.Cell>
          <Table.Cell minWidth={180}>
            <VisuallyHidden>N/A</VisuallyHidden>
          </Table.Cell>
          <Table.Cell width={120}>
            <VisuallyHidden>N/A</VisuallyHidden>
          </Table.Cell>
          <Table.Cell width={160} align="right">
            <Table.CellContent fontWeight="semibold">
              {formatCurrency(total.income)}
            </Table.CellContent>
          </Table.Cell>
          <Table.Cell width={160} align="right">
            <Table.CellContent fontWeight="semibold">
              {formatCurrency(total.tax)}
            </Table.CellContent>
          </Table.Cell>
          <Table.Cell width={160} align="right" stickyOffset={-0}>
            <Table.CellContent fontWeight="semibold">
              {formatCurrency(total.income - total.tax)}
            </Table.CellContent>
          </Table.Cell>
        </Table.Footer>
      </Table.Root>
    </Box>
  );
};
```

### Synced scroll

By default, `stickyOffset` is relative to nearest scrolling ancestor (the `Table` itself). If the table
doesn't scroll vertically within the overflowing container and you want the `Table.Header` or `Table.Footer` to stick while scrolling on the document window, use `syncScroll` property.

<Callout variant="tip">

When `syncScroll` is enabled, table's row groups become scrolling containers instead of the `Table` and their scrolling position is synced up.

More details about this technique are available in the following article: <https://uxdesign.cc/position-stuck-96c9f55d9526>

</Callout>

```jsx overflow=visible
() => {
  const data = React.useMemo(
    () => [
      {
        id: 1,
        name: 'Torvald Halvor',
        title: 'Finance manager',
        age: 56,
        city: 'Oslo',
        notes: 'Pastry chocolate jujubes cake cotton candy.'
      },
      {
        id: 2,
        name: 'Mikael Rikard',
        title: 'QA Engineer',
        age: 42,
        city: 'Berlin',
        notes: 'Pastry sweet roll sweet icing gummies chocolate.'
      },
      {
        id: 3,
        name: 'Ragnhild Tine',
        title: 'Software tester',
        age: 29,
        city: 'Oslo',
        notes: 'Fruitcake tart candy canes carrot cake jelly.'
      },
      {
        id: 4,
        name: 'Liv Margareta',
        title: 'Software developer',
        age: 35,
        city: 'Madrid',
        notes: 'Candy canes lollipop bear claw pudding jelly beans.'
      },
      {
        id: 5,
        name: 'Johnny Nowak',
        title: 'Intern',
        age: 24,
        city: 'Warsaw',
        notes: ''
      },
      {
        id: 6,
        name: 'Fritjof Snorre',
        title: 'Technical writer',
        age: 47,
        city: 'Trondheim',
        notes: 'Chocolate sweet candy halvah carrot cake soufflé.'
      },
      {
        id: 7,
        name: 'Angelica Ramos',
        title: 'UX/UI Designer',
        age: 30,
        city: 'Stockholm',
        notes: ''
      },
      {
        id: 8,
        name: 'Snorre Lauritz',
        title: 'Senior software developer',
        age: 52,
        city: 'London',
        notes: 'Tart jelly beans topping jelly liquorice pastry.'
      }
    ],
    []
  );

  return (
    <Box>
      <Table.Root aria-label="Example with synced scroll" syncScroll>
        <Table.Header stickyOffset={0}>
          <Table.ColumnHeader width={170} stickyOffset={0}>
            {'Name'}
          </Table.ColumnHeader>
          <Table.ColumnHeader minWidth={180}>
            {'Job position'}
          </Table.ColumnHeader>
          <Table.ColumnHeader width={120}>{'Age'}</Table.ColumnHeader>
          <Table.ColumnHeader width={160}>{'Office'}</Table.ColumnHeader>
          <Table.ColumnHeader minWidth={270}>{'Notes'}</Table.ColumnHeader>
        </Table.Header>
        <Table.Body>
          {data.map(row => (
            <Table.Row key={row.id}>
              <Table.Cell width={170} stickyOffset={0}>
                {row.name}
              </Table.Cell>
              <Table.Cell minWidth={180}>{row.title}</Table.Cell>
              <Table.Cell width={120}>{row.age}</Table.Cell>
              <Table.Cell width={160}>{row.city}</Table.Cell>
              <Table.Cell minWidth={270}>
                {row.notes ? (
                  row.notes
                ) : (
                  <Table.CellContent
                    fontSize="zeta"
                    color="foregroundNeutralSubtle"
                  >
                    {'—'}
                  </Table.CellContent>
                )}
              </Table.Cell>
            </Table.Row>
          ))}
        </Table.Body>
        <Table.Footer stickyOffset={0}>
          <Table.Cell role="rowheader">
            <Table.CellContent fontWeight="semibold">
              {'Total employees'}
            </Table.CellContent>
          </Table.Cell>
          <Table.Cell aria-colspan={3} align="right">
            <Table.CellContent fontWeight="semibold">
              {data.length}
            </Table.CellContent>
          </Table.Cell>
        </Table.Footer>
      </Table.Root>
    </Box>
  );
};
```

### Custom cell content

You may provide any custom content to `Table.ColumnHeader` and `Table.Cell`.

<Callout variant="tip">

Note that when providing `ReactText` (`string` and `number`) content to `Table.Cell`, the cells will benefit from improved re-render performance thanks to `React.memo`.

</Callout>

```jsx
<Table.Root
  aria-label="Example with custom cell content"
  highlightOnHover={false}
>
  <Table.Header>
    <Table.ColumnHeader width={170}>{'Name'}</Table.ColumnHeader>
    <Table.ColumnHeader minWidth={220}>{'Email'}</Table.ColumnHeader>
    <Table.ColumnHeader width={120}>{'State'}</Table.ColumnHeader>
    <Table.ColumnHeader minWidth={180}>{'Groups'}</Table.ColumnHeader>
    <Table.ColumnHeader width={60} stickyOffset={-0}>
      <VisuallyHidden>{'Action'}</VisuallyHidden>
    </Table.ColumnHeader>
  </Table.Header>
  <Table.Body>
    <Table.Row>
      <Table.Cell width={170}>{'Mikael Rikard'}</Table.Cell>
      <Table.Cell minWidth={220}>{'mikeal@foo-corp.com'}</Table.Cell>
      <Table.Cell width={120}>
        <Label color="info" variant="subtle" size="small" className="fwB">
          {'Pending'}
        </Label>
      </Table.Cell>
      <Table.Cell minWidth={180}>
        <Table.CellContent fontSize="zeta" color="foregroundNeutralSubtle">
          {'—'}
        </Table.CellContent>
      </Table.Cell>
      <Table.Cell width={60} align="center" stickyOffset={-0}>
        <Tooltip content="Edit" placement="top">
          <Button size="xSmall" variant="ghost" withLoneIcon>
            <SvgIcon iconName="edit" />
          </Button>
        </Tooltip>
      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={170}>{'Ragnhild Tine'}</Table.Cell>
      <Table.Cell minWidth={220}>{'ragnhild@foo-corp.com'}</Table.Cell>
      <Table.Cell width={120}>
        <Label color="success" variant="subtle" size="small" className="fwB">
          {'Active'}
        </Label>
      </Table.Cell>
      <Table.Cell minWidth={180}>
        <Flex wrap="wrap" gap="spacingXs">
          <Label variant="outline" size="small">
            {'Management'}
          </Label>
          <Label variant="outline" size="small">
            {'+1'}
          </Label>
        </Flex>
      </Table.Cell>
      <Table.Cell width={60} align="center" stickyOffset={-0}>
        <Tooltip content="Edit" placement="top">
          <Button size="xSmall" variant="ghost" withLoneIcon>
            <SvgIcon iconName="edit" />
          </Button>
        </Tooltip>
      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={170}>{'Fritjof Snorre'}</Table.Cell>
      <Table.Cell minWidth={220}>{'fritjof@foo-corp.com'}</Table.Cell>
      <Table.Cell width={120}>
        <Label color="success" variant="subtle" size="small" className="fwB">
          {'Active'}
        </Label>
      </Table.Cell>
      <Table.Cell minWidth={180}>
        <Flex wrap="wrap" gap="spacingXs">
          <Label variant="outline" size="small">
            {'Admin'}
          </Label>
          <Label variant="outline" size="small">
            {'Management'}
          </Label>
          <Label variant="outline" size="small">
            {'+2'}
          </Label>
        </Flex>
      </Table.Cell>
      <Table.Cell width={60} align="center" stickyOffset={-0}>
        <Tooltip content="Edit" placement="top">
          <Button size="xSmall" variant="ghost" withLoneIcon>
            <SvgIcon iconName="edit" />
          </Button>
        </Tooltip>
      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={170}>{'Snorre Lauritz'}</Table.Cell>
      <Table.Cell minWidth={220}>{'snorre@foo-corp.com'}</Table.Cell>
      <Table.Cell width={120}>
        <Label color="info" variant="subtle" size="small" className="fwB">
          {'Pending'}
        </Label>
      </Table.Cell>
      <Table.Cell minWidth={180}>
        <Flex wrap="wrap" gap="spacingXs">
          <Label variant="outline" size="small">
            {'Engineering'}
          </Label>
          <Label variant="outline" size="small">
            {'+1'}
          </Label>
        </Flex>
      </Table.Cell>
      <Table.Cell width={60} align="center" stickyOffset={-0}>
        <Tooltip content="Edit" placement="top">
          <Button size="xSmall" variant="ghost" withLoneIcon>
            <SvgIcon iconName="edit" />
          </Button>
        </Tooltip>
      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={170}>{'Torvald Halvor'}</Table.Cell>
      <Table.Cell minWidth={220}>{'torvald@foo-corp.com'}</Table.Cell>
      <Table.Cell width={120}>
        <Label color="danger" variant="subtle" size="small" className="fwB">
          {'Error'}
        </Label>
      </Table.Cell>
      <Table.Cell minWidth={180}>
        <Table.CellContent fontSize="zeta" color="foregroundNeutralSubtle">
          {'—'}
        </Table.CellContent>
      </Table.Cell>
      <Table.Cell width={60} align="center" stickyOffset={-0}>
        <Tooltip content="Edit" placement="top">
          <Button size="xSmall" variant="ghost" withLoneIcon>
            <SvgIcon iconName="edit" />
          </Button>
        </Tooltip>
      </Table.Cell>
    </Table.Row>
  </Table.Body>
</Table.Root>
```

Each individual table cell can support multiple rows of text. This is useful when you want to provide related details in a table without adding additional columns, such as placing an employee's email address beneath their name.

```jsx
<Table.Root
  aria-label="Example with custom cell content"
  highlightOnHover={false}
  overflowStrategy="wrap"
>
  <Table.Header>
    <Table.ColumnHeader width={250}>{'Employee'}</Table.ColumnHeader>
    <Table.ColumnHeader minWidth={200}>{'Language skills'}</Table.ColumnHeader>
    <Table.ColumnHeader width={140}>{'Is contractor?'}</Table.ColumnHeader>
    <Table.ColumnHeader width={80} align="right">
      {'Tasks'}
    </Table.ColumnHeader>
  </Table.Header>
  <Table.Body>
    <Table.Row>
      <Table.Cell width={250}>
        <Flex>
          <Avatar.Root of="Mikael Rikard">
            <Avatar.Initials />
          </Avatar.Root>
          <Box>
            <Text as="div" aria-hidden>
              {'Mikael Rikard'}
            </Text>
            <Text as="div" color="foregroundNeutralSubtle">
              {'mikeal@foo-corp.com'}
            </Text>
          </Box>
        </Flex>
      </Table.Cell>
      <Table.Cell minWidth={200}>{'Norwegian, English, French'}</Table.Cell>
      <Table.Cell width={140}>
        <VisuallyHidden>{'No'}</VisuallyHidden>
      </Table.Cell>
      <Table.Cell width={80} align="right">
        {'8'}
      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={250}>
        <Flex>
          <Avatar.Root of="Angelica Ramos">
            <Avatar.Initials />
          </Avatar.Root>
          <Box>
            <Text as="div">{'Angelica Ramos'}</Text>
            <Text as="div" color="foregroundNeutralSubtle">
              {'angelica@foo-corp.com'}
            </Text>
          </Box>
        </Flex>
      </Table.Cell>
      <Table.Cell minWidth={200}>{'Spanish, English'}</Table.Cell>
      <Table.Cell width={140}>
        <SvgIcon iconName="check" title="Yes" />
      </Table.Cell>
      <Table.Cell width={80} align="right">
        {'15'}
      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={250}>
        <Flex>
          <Avatar.Root of="Liv Margareta">
            <Avatar.Initials />
          </Avatar.Root>
          <Box>
            <Text as="div" aria-hidden>
              {'Liv Margareta'}
            </Text>
            <Text as="div" color="foregroundNeutralSubtle">
              {'liv@foo-corp.com'}
            </Text>
          </Box>
        </Flex>
      </Table.Cell>
      <Table.Cell minWidth={200}>{'English, Norwegian'}</Table.Cell>
      <Table.Cell width={140}>
        <VisuallyHidden>{'No'}</VisuallyHidden>
      </Table.Cell>
      <Table.Cell width={80} align="right">
        {'25'}
      </Table.Cell>
    </Table.Row>
  </Table.Body>
</Table.Root>
```

### Header with title & subtitle

`Table.ColumnHeaderTitle` and `Table.ColumnHeaderSubtitle` allow you to separate the title and subtitle of the column header. This is useful when you want to provide additional context or information about the column's content, such as unit information to avoid repeating it in each cell.

```jsx
() => {
  const data = React.useMemo(
    () => [
      {
        id: 1,
        tradedQuantity: 35,
        totalQuantity: 70,
        limitPrice: 125.33,
        avgSellPrice: 105.76
      },
      {
        id: 2,
        tradedQuantity: 2,
        totalQuantity: 12,
        limitPrice: 105.76,
        avgSellPrice: 99.3
      },
      {
        id: 3,
        tradedQuantity: 3.5,
        totalQuantity: 20,
        limitPrice: 210.5,
        avgSellPrice: 128.78
      },
      {
        id: 4,
        tradedQuantity: 0,
        totalQuantity: 123,
        limitPrice: 180,
        avgSellPrice: 104.21
      }
    ],
    []
  );

  const formatPrice = value =>
    value.toLocaleString('en-GB', {
      minimumFractionDigits: 2
    });

  const formatQuantity = value =>
    value.toLocaleString('en-GB', {
      minimumFractionDigits: 1
    });

  return (
    <Table.Root aria-label="Example with title and subtitle">
      <Table.Header>
        <Table.ColumnHeader width={100}>{'Order ID'}</Table.ColumnHeader>
        <Table.ColumnHeader minWidth={160} align="right">
          <Table.ColumnHeaderTitle>{'Traded quantity'}</Table.ColumnHeaderTitle>
          <Table.ColumnHeaderSubtitle>{'MW'}</Table.ColumnHeaderSubtitle>
        </Table.ColumnHeader>
        <Table.ColumnHeader minWidth={160} align="right">
          <Table.ColumnHeaderTitle>{'Total quantity'}</Table.ColumnHeaderTitle>
          <Table.ColumnHeaderSubtitle>{'MW'}</Table.ColumnHeaderSubtitle>
        </Table.ColumnHeader>
        <Table.ColumnHeader minWidth={160} align="right">
          <Table.ColumnHeaderTitle>{'Limit price'}</Table.ColumnHeaderTitle>
          <Table.ColumnHeaderSubtitle>{'EUR/MWh'}</Table.ColumnHeaderSubtitle>
        </Table.ColumnHeader>
        <Table.ColumnHeader minWidth={160} align="right">
          <Table.ColumnHeaderTitle>{'Avg sell price'}</Table.ColumnHeaderTitle>
          <Table.ColumnHeaderSubtitle>{'EUR/MWh'}</Table.ColumnHeaderSubtitle>
        </Table.ColumnHeader>
      </Table.Header>
      <Table.Body>
        {data.map(row => (
          <Table.Row key={row.id}>
            <Table.Cell width={100}>{row.id}</Table.Cell>
            <Table.Cell minWidth={160} align="right">
              {formatQuantity(row.tradedQuantity)}
            </Table.Cell>
            <Table.Cell minWidth={160} align="right">
              {formatQuantity(row.totalQuantity)}
            </Table.Cell>
            <Table.Cell minWidth={160} align="right">
              {formatPrice(row.limitPrice)}
            </Table.Cell>
            <Table.Cell minWidth={160} align="right">
              {formatPrice(row.avgSellPrice)}
            </Table.Cell>
          </Table.Row>
        ))}
      </Table.Body>
    </Table.Root>
  );
};
```

### Row actions

Row actions can be represented as icon buttons. When there is only one action available, you may provide a [tooltip](/components/tooltip) for better clarity.

```jsx
<Table.Root aria-label="Example with row actions">
  <Table.Header>
    <Table.ColumnHeader width={200}>{'Name'}</Table.ColumnHeader>
    <Table.ColumnHeader minWidth={240}>{'Email'}</Table.ColumnHeader>
    <Table.ColumnHeader width={60}>
      <VisuallyHidden>{'Action'}</VisuallyHidden>
    </Table.ColumnHeader>
  </Table.Header>
  <Table.Body>
    <Table.Row>
      <Table.Cell width={200}>{'Mikael Rikard'}</Table.Cell>
      <Table.Cell minWidth={240}>{'mikeal@foo-corp.com'}</Table.Cell>
      <Table.Cell width={60} align="right">
        <Tooltip content="View" placement="top">
          <Button size="xSmall" variant="ghost" withLoneIcon>
            <SvgIcon iconName="show" />
          </Button>
        </Tooltip>
      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={200}>{'Angelica Ramos'}</Table.Cell>
      <Table.Cell minWidth={240}>{'angelica@foo-corp.com'}</Table.Cell>
      <Table.Cell width={60} align="right">
        <Tooltip content="View" placement="top">
          <Button size="xSmall" variant="ghost" withLoneIcon>
            <SvgIcon iconName="show" />
          </Button>
        </Tooltip>
      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={200}>{'Liv Margareta'}</Table.Cell>
      <Table.Cell minWidth={240}>{'liv@foo-corp.com'}</Table.Cell>
      <Table.Cell width={60} align="right">
        <Tooltip content="View" placement="top">
          <Button size="xSmall" variant="ghost" withLoneIcon>
            <SvgIcon iconName="show" />
          </Button>
        </Tooltip>
      </Table.Cell>
    </Table.Row>
  </Table.Body>
</Table.Root>
```

When multiple actions are available on the row, use [Menu component](/components/menu) for displaying list of actions.

```jsx
<Table.Root aria-label="Example with multiple row actions">
  <Table.Header>
    <Table.ColumnHeader width={200}>{'Name'}</Table.ColumnHeader>
    <Table.ColumnHeader minWidth={240}>{'Email'}</Table.ColumnHeader>
    <Table.ColumnHeader width={60}>
      <VisuallyHidden>{'Actions'}</VisuallyHidden>
    </Table.ColumnHeader>
  </Table.Header>
  <Table.Body>
    <Table.Row>
      <Table.Cell width={200}>{'Mikael Rikard'}</Table.Cell>
      <Table.Cell minWidth={240}>{'mikeal@foo-corp.com'}</Table.Cell>
      <Table.Cell width={60} align="right">
        <Menu.Root>
          <Menu.Trigger
            as={Button}
            withLoneIcon
            aria-label="Row actions"
            size="xSmall"
            variant="ghost"
          >
            <SvgIcon iconName="more" />
          </Menu.Trigger>
          <Menu.Content anchorOffset={4}>
            <Menu.Item
              startElement={<SvgIcon iconName="show" />}
              onSelect={() => {}}
            >
              {'View'}
            </Menu.Item>
            <Menu.Item
              startElement={<SvgIcon iconName="edit" />}
              onSelect={() => {}}
            >
              {'Edit'}
            </Menu.Item>
            <Menu.Separator />
            <Menu.Item
              intent="danger"
              startElement={<SvgIcon iconName="trash" />}
              onSelect={() => {}}
            >
              {'Delete'}
            </Menu.Item>
          </Menu.Content>
        </Menu.Root>
      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={200}>{'Angelica Ramos'}</Table.Cell>
      <Table.Cell minWidth={240}>{'angelica@foo-corp.com'}</Table.Cell>
      <Table.Cell width={60} align="right">
        <Menu.Root>
          <Menu.Trigger
            as={Button}
            withLoneIcon
            aria-label="Row actions"
            size="xSmall"
            variant="ghost"
          >
            <SvgIcon iconName="more" />
          </Menu.Trigger>
          <Menu.Content anchorOffset={4}>
            <Menu.Item
              startElement={<SvgIcon iconName="show" />}
              onSelect={() => {}}
            >
              {'View'}
            </Menu.Item>
            <Menu.Item
              startElement={<SvgIcon iconName="edit" />}
              onSelect={() => {}}
            >
              {'Edit'}
            </Menu.Item>
            <Menu.Separator />
            <Menu.Item
              intent="danger"
              startElement={<SvgIcon iconName="trash" />}
              onSelect={() => {}}
            >
              {'Delete'}
            </Menu.Item>
          </Menu.Content>
        </Menu.Root>
      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={200}>{'Liv Margareta'}</Table.Cell>
      <Table.Cell minWidth={240}>{'liv@foo-corp.com'}</Table.Cell>
      <Table.Cell width={60} align="right">
        <Menu.Root>
          <Menu.Trigger
            as={Button}
            withLoneIcon
            aria-label="Row actions"
            size="xSmall"
            variant="ghost"
          >
            <SvgIcon iconName="more" />
          </Menu.Trigger>
          <Menu.Content anchorOffset={4}>
            <Menu.Item
              startElement={<SvgIcon iconName="show" />}
              onSelect={() => {}}
            >
              {'View'}
            </Menu.Item>
            <Menu.Item
              startElement={<SvgIcon iconName="edit" />}
              onSelect={() => {}}
            >
              {'Edit'}
            </Menu.Item>
            <Menu.Separator />
            <Menu.Item
              intent="danger"
              startElement={<SvgIcon iconName="trash" />}
              onSelect={() => {}}
            >
              {'Delete'}
            </Menu.Item>
          </Menu.Content>
        </Menu.Root>
      </Table.Cell>
    </Table.Row>
  </Table.Body>
</Table.Root>
```

In some cases, you may want to attach an action to an entire row.

```jsx
<Table.Root aria-label="Example with clickable rows">
  <Table.Header>
    <Table.ColumnHeader width={200}>{'Name'}</Table.ColumnHeader>
    <Table.ColumnHeader minWidth={240}>{'Email'}</Table.ColumnHeader>
    <Table.ColumnHeader width={48} />
  </Table.Header>
  <Table.Body>
    <Table.Row className="is-actionable" onClick={() => {}}>
      <Table.Cell width={200}>{'Mikael Rikard'}</Table.Cell>
      <Table.Cell minWidth={240}>{'mikeal@foo-corp.com'}</Table.Cell>
      <Table.Cell width={48} align="right">
        <SvgIcon iconName="chevronRight" />
      </Table.Cell>
    </Table.Row>
    <Table.Row className="is-actionable" onClick={() => {}}>
      <Table.Cell width={200}>{'Angelica Ramos'}</Table.Cell>
      <Table.Cell minWidth={240}>{'angelica@foo-corp.com'}</Table.Cell>
      <Table.Cell width={48} align="right">
        <SvgIcon iconName="chevronRight" />
      </Table.Cell>
    </Table.Row>
    <Table.Row className="is-actionable" onClick={() => {}}>
      <Table.Cell width={200}>{'Liv Margareta'}</Table.Cell>
      <Table.Cell minWidth={240}>{'liv@foo-corp.com'}</Table.Cell>
      <Table.Cell width={48} align="right">
        <SvgIcon iconName="chevronRight" />
      </Table.Cell>
    </Table.Row>
  </Table.Body>
</Table.Root>
```

### Highlighting rows

You can highlight individual rows with `isHighlighted` property. Highlights provide additional visual prominence to a row and is useful to quickly differentiate it from other rows.

<Callout variant="tip">

Keep in mind that users with visual impairments may not notice when rows are highlighted, so prefer not to rely on highlights alone to convey information.

</Callout>

<Callout variant="important">

Never use highlighted rows to indicate that a user has selected the row. Refer to [rows selection example](#rows-selection) instead.

</Callout>

```jsx
<Table.Root aria-label="Example with highlighted row">
  <Table.Header>
    <Table.ColumnHeader width={170}>{'User'}</Table.ColumnHeader>
    <Table.ColumnHeader minWidth={220}>{'Email'}</Table.ColumnHeader>
    <Table.ColumnHeader minWidth={120}>{'Account status'}</Table.ColumnHeader>
  </Table.Header>
  <Table.Body>
    <Table.Row>
      <Table.Cell width={170}>{'Ragnhild Tine'}</Table.Cell>
      <Table.Cell minWidth={220}>{'ragnhild@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'User'}</Table.Cell>
    </Table.Row>
    <Table.Row isHighlighted>
      <Table.Cell width={170}>{'Fritjof Snorre'}</Table.Cell>
      <Table.Cell minWidth={220}>{'fritjof@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'User'}</Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={170}>{'Liv Margareta'}</Table.Cell>
      <Table.Cell minWidth={220}>{'liv@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'User'}</Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={170}>{'Arnold Bretz'}</Table.Cell>
      <Table.Cell minWidth={220}>{'arnold@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'User'}</Table.Cell>
    </Table.Row>
  </Table.Body>
</Table.Root>
```

### Row tones

`Table.Row` supports setting `tone` property.

Tones are typically used to emphasize the status of a row and facilitate users in identifying which parts of the table require attention.

Supported tones include:

- `danger` - for statuses that imply error, failure or problem.
- `success` - for statuses that are positive, e.g. confirmations.
- `warning` - for statuses that are in-progress, pending, or that could require user intervention.

<Callout variant="tip">

Be sure to utilize tones purposefully to convey important statuses. Excessive use of tones can lead to noisy interface with too many elements pulling user attention.

Row tones should not be used alone as the only indicator of status.
Be sure to include actual status labels in the table itself when possible.

</Callout>

```jsx
<Table.Root aria-label="Example with row tones">
  <Table.Header>
    <Table.ColumnHeader width={170}>{'User'}</Table.ColumnHeader>
    <Table.ColumnHeader minWidth={220}>{'Email'}</Table.ColumnHeader>
    <Table.ColumnHeader minWidth={120}>{'Account status'}</Table.ColumnHeader>
  </Table.Header>
  <Table.Body>
    <Table.Row>
      <Table.Cell width={170}>{'Mikael Rikard'}</Table.Cell>
      <Table.Cell minWidth={220}>{'mikeal@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'User'}</Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={170}>{'Ragnhild Tine'}</Table.Cell>
      <Table.Cell minWidth={220}>{'ragnhild@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'User'}</Table.Cell>
    </Table.Row>
    <Table.Row tone="success">
      <Table.Cell width={170}>{'Fritjof Snorre'}</Table.Cell>
      <Table.Cell minWidth={220}>{'fritjof@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'Activated'}</Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={170}>{'Liv Margareta'}</Table.Cell>
      <Table.Cell minWidth={220}>{'liv@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'User'}</Table.Cell>
    </Table.Row>
    <Table.Row tone="warning">
      <Table.Cell width={170}>{'Snorre Lauritz'}</Table.Cell>
      <Table.Cell minWidth={220}>{'snorre@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'Waiting for activation'}</Table.Cell>
    </Table.Row>
    <Table.Row tone="danger">
      <Table.Cell width={170}>{'Torvald Halvor'}</Table.Cell>
      <Table.Cell minWidth={220}>{'torvald@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'Activation error'}</Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={170}>{'Arnold Bretz'}</Table.Cell>
      <Table.Cell minWidth={220}>{'arnold@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'User'}</Table.Cell>
    </Table.Row>
  </Table.Body>
</Table.Root>
```

Note that you can provide a custom tone by using `tone="custom"` property and assigning specific color to `--table-cell-bg-color` CSS variable.

```jsx
<Table.Root aria-label="Example with custom row tone">
  <Table.Header>
    <Table.ColumnHeader width={170}>{'User'}</Table.ColumnHeader>
    <Table.ColumnHeader minWidth={220}>{'Email'}</Table.ColumnHeader>
    <Table.ColumnHeader minWidth={120}>{'Account status'}</Table.ColumnHeader>
  </Table.Header>
  <Table.Body>
    <Table.Row>
      <Table.Cell width={170}>{'Mikael Rikard'}</Table.Cell>
      <Table.Cell minWidth={220}>{'mikeal@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'User'}</Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={170}>{'Ragnhild Tine'}</Table.Cell>
      <Table.Cell minWidth={220}>{'ragnhild@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'User'}</Table.Cell>
    </Table.Row>
    <Table.Row
      tone="custom"
      css={{ '--table-cell-bg-color': '$colors$dataLazuli20' }}
    >
      <Table.Cell width={170}>{'Lutz Karlslag'}</Table.Cell>
      <Table.Cell minWidth={220}>{'lutz@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'Admin'}</Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell width={170}>{'Duncan Karlep'}</Table.Cell>
      <Table.Cell minWidth={220}>{'duncan@foo-corp.com'}</Table.Cell>
      <Table.Cell minWidth={120}>{'User'}</Table.Cell>
    </Table.Row>
  </Table.Body>
</Table.Root>
```

### Cell tones

Similar to [row tones](#row-tones), a single cell also supports setting `tone` property. This can be useful to grab a user’s attention on a specific cell(s) in the table.

<Callout variant="tip">

Be sure to utilize tones purposefully to convey important statuses. Overuse of tones can dilute attention on what matters most.

</Callout>

<Callout variant="tip">

When using `Table` with cell tones, you may want to visually separate elements using `showCellSeparation` property to enhance visual structure of the table.

</Callout>

```jsx
() => {
  const formatCurrency = value =>
    value.toLocaleString('en-GB', { style: 'currency', currency: 'EUR' });

  const [state, setState] = React.useState(true);

  return (
    <Flex flow="column">
      <Switch.Root isActive={state} onIsActiveChange={setState}>
        <Switch.Label>{'Show cell separation'}</Switch.Label>
        <Switch.Indicator>
          <Switch.On>{'On'}</Switch.On>
          <Switch.Off>{'Off'}</Switch.Off>
        </Switch.Indicator>
      </Switch.Root>
      <Table.Root
        aria-label="Example with cells tone"
        rowSeparation={'stripes'}
        showCellSeparation={state}
      >
        <Table.Header>
          <Table.ColumnHeader width={280}>{'Position'}</Table.ColumnHeader>
          <Table.ColumnHeader width={150}>{'Status'}</Table.ColumnHeader>
          <Table.ColumnHeader minWidth={120}>{'Area'}</Table.ColumnHeader>
          <Table.ColumnHeader width={150}>{'Balance'}</Table.ColumnHeader>
        </Table.Header>
        <Table.Body>
          <Table.Row>
            <Table.Cell width={280}>{'Monitoring service'}</Table.Cell>
            <Table.Cell width={150}>{'Active'}</Table.Cell>
            <Table.Cell minWidth={120}>{'Europe'}</Table.Cell>
            <Table.Cell tone="success" width={150}>
              {formatCurrency(2045)}
            </Table.Cell>
          </Table.Row>
          <Table.Row>
            <Table.Cell width={280}>{'Monitoring service'}</Table.Cell>
            <Table.Cell width={150}>{'Active'}</Table.Cell>
            <Table.Cell minWidth={120}>{'Asia'}</Table.Cell>
            <Table.Cell tone="success" width={150}>
              {formatCurrency(11530)}
            </Table.Cell>
          </Table.Row>
          <Table.Row>
            <Table.Cell width={280}>{'Deployment stacks'}</Table.Cell>
            <Table.Cell width={150}>{'Active'}</Table.Cell>
            <Table.Cell minWidth={120}>{'Europe'}</Table.Cell>
            <Table.Cell
              tone="custom"
              css={{ '--table-cell-bg-color': '$colors$colorGray10' }}
              width={150}
            >
              {formatCurrency(-132)}
            </Table.Cell>
          </Table.Row>
          <Table.Row>
            <Table.Cell width={280}>{'Diagnostic tools'}</Table.Cell>
            <Table.Cell width={150} tone="danger">
              {'Error'}
            </Table.Cell>
            <Table.Cell minWidth={120}>{'Europe'}</Table.Cell>
            <Table.Cell tone="success" width={150}>
              {formatCurrency(1678)}
            </Table.Cell>
          </Table.Row>
          <Table.Row>
            <Table.Cell width={280}>{'Cloud computation'}</Table.Cell>
            <Table.Cell width={150}>{'Active'}</Table.Cell>
            <Table.Cell minWidth={120}>{'Europe'}</Table.Cell>
            <Table.Cell tone="danger" width={150}>
              {formatCurrency(-5320)}
            </Table.Cell>
          </Table.Row>
          <Table.Row>
            <Table.Cell width={280}>{'Storage systems'}</Table.Cell>
            <Table.Cell width={150} tone="warning">
              {'Pending'}
            </Table.Cell>
            <Table.Cell minWidth={120}>{'Europe'}</Table.Cell>
            <Table.Cell tone="danger" width={150}>
              {formatCurrency(-25934)}
            </Table.Cell>
          </Table.Row>
          <Table.Row>
            <Table.Cell width={280}>{'Application traffic'}</Table.Cell>
            <Table.Cell width={150} tone="warning">
              {'Pending'}
            </Table.Cell>
            <Table.Cell minWidth={120}>{'Europe'}</Table.Cell>
            <Table.Cell tone="success" width={150}>
              {formatCurrency(4318)}
            </Table.Cell>
          </Table.Row>
        </Table.Body>
      </Table.Root>
    </Flex>
  );
};
```

### Loading state

Rather than using a [Spinner component](/components/spinner), you can use the [Skeleton placeholders](/components/skeleton) in the cells to create a more pleasant loading experience.

```jsx
() => {
  const data = [
    ['538902', 'Golf club', 'Payment pending', '3:45'],
    ['570444', 'Running shoes', 'Completed', '11:00'],
    ['100953', 'Squash racquet', 'Completed', '11:38'],
    ['810245', 'Football jersey', 'Pending payment', '15:00']
  ];

  const [isLoading, setIsLoading] = React.useState(false);

  const handleToggleLoading = () => {
    setIsLoading(prevIsLoading => !prevIsLoading);
  };

  return (
    <Flex flow="column">
      <Box>
        <Button variant="outline" onClick={handleToggleLoading}>
          {'Toggle loading state'}
        </Button>
      </Box>
      <Table.Root aria-label="Table in a loading state">
        <Table.Header>
          <Table.ColumnHeader>{'Product ID'}</Table.ColumnHeader>
          <Table.ColumnHeader>{'Name'}</Table.ColumnHeader>
          <Table.ColumnHeader>{'Status'}</Table.ColumnHeader>
          <Table.ColumnHeader>{'Time'}</Table.ColumnHeader>
        </Table.Header>
        <Table.Body>
          {data.map((row, rowIndex) => (
            <Table.Row key={rowIndex}>
              {row.map((cell, cellIndex) => (
                <Table.Cell key={cellIndex}>
                  <Skeleton isLoaded={!isLoading}>
                    <Table.CellContent>{cell}</Table.CellContent>
                  </Skeleton>
                </Table.Cell>
              ))}
            </Table.Row>
          ))}
        </Table.Body>
      </Table.Root>
    </Flex>
  );
};
```

### Empty state

When there is no data to feed the table or no match can be found while filtering, render `Table.EmptyState` instead of `Table.Body` to provide additional context for the user.

For example, if a table of users isn’t populated yet, the empty state within the table shows the user what data to expect and optionally offers an action to populate the table.

```jsx
() => {
  const data = [];

  return (
    <Table.Root aria-label="Table empty state example">
      <Table.Header>
        <Table.ColumnHeader>{'Name'}</Table.ColumnHeader>
        <Table.ColumnHeader>{'Job position'}</Table.ColumnHeader>
        <Table.ColumnHeader>{'Age'}</Table.ColumnHeader>
        <Table.ColumnHeader>{'Office'}</Table.ColumnHeader>
      </Table.Header>
      {data.length > 0 ? (
        <Table.Body>
          {data.map((row, rowIndex) => (
            <Table.Row key={rowIndex}>
              {row.map((cell, cellIndex) => (
                <Table.Cell key={cellIndex}>{cell}</Table.Cell>
              ))}
            </Table.Row>
          ))}
        </Table.Body>
      ) : (
        <Table.EmptyState>
          <Flex cross="center" flow="column" gap="spacingS">
            <Heading as="h3">{'There are no users yet'}</Heading>
            <Text
              as="p"
              marginTop="none"
              marginBottom="spacingS"
              color="foregroundNeutralSubtle"
            >
              {'Users will show up here as they have been added.'}
            </Text>
            <Button leftIcon={<SvgIcon iconName="add" />} onClick={() => {}}>
              {'Add new user'}
            </Button>
          </Flex>
        </Table.EmptyState>
      )}
    </Table.Root>
  );
};
```

### Table inside a card

The table can be contextually styled to better fit the container it is placed into, such as [Card](/components/card).

<Callout variant="tip">

Use `addHorizontalWhitespace` property to add additional horizontal padding on the outer table cells equal to the card padding (or equal to the whitespace between adjacent cells when placed outside a card).

</Callout>

```jsx
<Card.Root elevated css={{ overflow: 'hidden' }}>
  <Card.Header>Users</Card.Header>
  <Card.Body paddingX="none" paddingBottom="none">
    <Table.Root
      aria-label="Table inside a card"
      outlined={false}
      addHorizontalWhitespace
    >
      <Table.Header>
        <Table.ColumnHeader width={200}>{'Name'}</Table.ColumnHeader>
        <Table.ColumnHeader minWidth={240}>{'Email'}</Table.ColumnHeader>
        <Table.ColumnHeader width={72}>
          <VisuallyHidden>{'Action'}</VisuallyHidden>
        </Table.ColumnHeader>
      </Table.Header>
      <Table.Body>
        <Table.Row>
          <Table.Cell width={200}>{'Mikael Rikard'}</Table.Cell>
          <Table.Cell minWidth={240}>{'mikeal@foo-corp.com'}</Table.Cell>
          <Table.Cell width={72} align="right">
            <Tooltip content="View" placement="top">
              <Button size="xSmall" variant="ghost" withLoneIcon>
                <SvgIcon iconName="show" />
              </Button>
            </Tooltip>
          </Table.Cell>
        </Table.Row>
        <Table.Row>
          <Table.Cell width={200}>{'Angelica Ramos'}</Table.Cell>
          <Table.Cell minWidth={240}>{'angelica@foo-corp.com'}</Table.Cell>
          <Table.Cell width={72} align="right">
            <Tooltip content="View" placement="top">
              <Button size="xSmall" variant="ghost" withLoneIcon>
                <SvgIcon iconName="show" />
              </Button>
            </Tooltip>
          </Table.Cell>
        </Table.Row>
        <Table.Row>
          <Table.Cell width={200}>{'Liv Margareta'}</Table.Cell>
          <Table.Cell minWidth={240}>{'liv@foo-corp.com'}</Table.Cell>
          <Table.Cell width={72} align="right">
            <Tooltip content="View" placement="top">
              <Button size="xSmall" variant="ghost" withLoneIcon>
                <SvgIcon iconName="show" />
              </Button>
            </Tooltip>
          </Table.Cell>
        </Table.Row>
      </Table.Body>
    </Table.Root>
  </Card.Body>
</Card.Root>
```

### Overflowing cell content

You may use `useHasOverflow` hook to dynamically apply [Tooltip](/components/tooltip) only when content overflows `Table.CellContent` area.
The tooltip will show full content when hovered over the abbreviated cell text.

<Callout variant="tip">

Try manipulating cell widths or wrap long content into multiple lines with `overflowStrategy` set to `wrap` to prevent text content overflowing the cell.

</Callout>

<Accordion>
  <AccordionItem title={<InlineCode>TableCellContentWithTooltip</InlineCode>}>

```jsx
import { useHasOverflow } from '@gemini-suite/vera-react';

const TableCellContentWithTooltip = React.memo(({ children }) => {
  const [cellContentRef, hasHorizontalOverflow] = useHasOverflow({
    observeMutations: true
  });

  return hasHorizontalOverflow ? (
    <Tooltip content={children} css={{ maxWidth: 320 }}>
      <Table.CellContent ref={cellContentRef}>{children}</Table.CellContent>
    </Tooltip>
  ) : (
    <Table.CellContent ref={cellContentRef}>{children}</Table.CellContent>
  );
});
```

  </AccordionItem>
</Accordion>

```jsx
() => {
  const data = [
    ['Torvald Halvor', 'Shortbread gummies chupa chups.', 'Madrid'],
    [
      'Mikael Rikard',
      'Brownie icing jelly beans macaroon apple pie bear claw candy canes spaghetti bolognese with toffee topping.',
      'Berlin'
    ],
    [
      'Ragnhild Tine',
      'Lollipop dessert lemon drops croissant donut onion rings.',
      'Oslo'
    ],
    [
      'Snorre Lauritz',
      'Fruitcake tart candy canes carrot cake jelly chocolate sweet candy halvah carrot cake soufflé.',
      'London'
    ]
  ];

  return (
    <Table.Root aria-label="Table with cell tooltip example">
      <Table.Header>
        <Table.ColumnHeader width={160}>{'Name'}</Table.ColumnHeader>
        <Table.ColumnHeader minWidth={320}>{'Comment'}</Table.ColumnHeader>
        <Table.ColumnHeader width={160}>{'Office'}</Table.ColumnHeader>
      </Table.Header>
      <Table.Body>
        {data.map((row, rowIndex) => (
          <Table.Row key={rowIndex}>
            {row.map((cell, cellIndex) => {
              if (cellIndex === 1) {
                return (
                  <Table.Cell minWidth={320} key={cellIndex}>
                    <TableCellContentWithTooltip>
                      {cell}
                    </TableCellContentWithTooltip>
                  </Table.Cell>
                );
              }

              return (
                <Table.Cell width={160} key={cellIndex}>
                  {cell}
                </Table.Cell>
              );
            })}
          </Table.Row>
        ))}
      </Table.Body>
    </Table.Root>
  );
};
```

### Drag and drop

With libraries such as `react-dnd` you can easily implement drag-and-drop feature over table rows and change their order.

<Callout variant="tip">

Don't forget to wrap your App into `DndProvider` with appropriate `backend` value.

Check [react-dnd docs](https://react-dnd.github.io/react-dnd/docs/overview) for more information.

```jsx lineHighlight=6,8
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';

function App() {
  return (
    <DndProvider backend={HTML5Backend}>
      <Main />
    </DndProvider>
  );
}
```

</Callout>

`react-dnd` provides a way to separate `dragRef` and `dropRef` to use them on different DOM elements. It can be useful when there are multiple actionable elements in table rows.

<Accordion>
  <AccordionItem title={<InlineCode>DraggableRowWithHandle</InlineCode>}>

```jsx
import { useDrag, useDrop } from 'react-dnd';
import { useForkRef } from '@gemini-suite/vera-react';

const DraggableRowWithHandle = React.forwardRef(
  ({ row, reorderRow, children, ...delegated }, forwardedRef) => {
    const [{ isOver }, dropRef] = useDrop({
      accept: 'row',
      drop: draggedRow => reorderRow(draggedRow.id, row.id),
      collect: monitor => ({
        isOver: monitor.isOver()
      })
    });

    const [{ isDragging }, dragRef, previewRef] = useDrag({
      item: { id: row.id },
      type: 'row',
      collect: monitor => ({
        isDragging: monitor.isDragging()
      })
    });

    const composedRef = useForkRef(forwardedRef, dropRef, previewRef);

    const tableRowStatus = isDragging
      ? 'dragging'
      : isOver
        ? 'dropTarget'
        : undefined;

    return (
      <Table.Row ref={composedRef} {...delegated} status={tableRowStatus}>
        <Table.Cell width={48} ref={dragRef} css={{ cursor: 'grab' }}>
          <SvgIcon iconName="dragAndDrop" />
        </Table.Cell>
        {children}
      </Table.Row>
    );
  }
);
```

  </AccordionItem>
</Accordion>

```jsx
() => {
  const [data, setData] = React.useState([
    {
      id: 1,
      name: 'Torvald Halvor',
      comment:
        'Shortbread gummies bear claw liquorice liquorice candy chupa chups cheesecake gingerbread.',
      office: 'Oslo'
    },
    {
      id: 2,
      name: 'Mikael Rikard',
      comment:
        'Brownie icing jelly beans macaroon apple pie bear claw candy canes.',
      office: 'Berlin'
    },
    {
      id: 3,
      name: 'Ragnhild Tine',
      comment: 'Gummi bears lollipop biscuit apple pie pudding wafer.',
      office: 'Munich'
    },
    {
      id: 4,
      name: 'Jan Kowalski',
      comment: 'Liquorice candy chupa chups cheesecake.',
      office: 'Warsaw'
    }
  ]);

  const reorderRow = (draggedRowId, targetRowId) => {
    const copy = [...data];
    const firstIndex = copy.findIndex(a => a.id === draggedRowId);
    const secondIndex = copy.findIndex(a => a.id === targetRowId);
    [copy[firstIndex], copy[secondIndex]] = [
      copy[secondIndex],
      copy[firstIndex]
    ];
    setData(copy);
  };

  return (
    <Table.Root aria-label="Drag and drop table">
      <Table.Header>
        <Table.ColumnHeader width={48}>
          <VisuallyHidden>{'Drag'}</VisuallyHidden>
        </Table.ColumnHeader>
        <Table.ColumnHeader maxWidth={60}>{'ID'}</Table.ColumnHeader>
        <Table.ColumnHeader>{'Name'}</Table.ColumnHeader>
        <Table.ColumnHeader>{'Comment'}</Table.ColumnHeader>
        <Table.ColumnHeader>{'Office'}</Table.ColumnHeader>
        <Table.ColumnHeader width={60}>
          <VisuallyHidden>{'Actions'}</VisuallyHidden>
        </Table.ColumnHeader>
      </Table.Header>
      <Table.Body>
        {data.map(row => (
          <DraggableRowWithHandle
            key={row.id}
            row={row}
            reorderRow={reorderRow}
          >
            <Table.Cell maxWidth={60}>{row.id}</Table.Cell>
            <Table.Cell>{row.name}</Table.Cell>
            <Table.Cell>{row.comment}</Table.Cell>
            <Table.Cell>{row.office}</Table.Cell>
            <Table.Cell width={60} align="right">
              <Menu.Root>
                <Menu.Trigger
                  as={Button}
                  withLoneIcon
                  aria-label="Row actions"
                  size="xSmall"
                  variant="ghost"
                >
                  <SvgIcon iconName="more" />
                </Menu.Trigger>
                <Menu.Content>
                  <Menu.Item
                    startElement={<SvgIcon iconName="edit" />}
                    onSelect={() => {}}
                  >
                    {'Edit'}
                  </Menu.Item>
                </Menu.Content>
              </Menu.Root>
            </Table.Cell>
          </DraggableRowWithHandle>
        ))}
      </Table.Body>
    </Table.Root>
  );
};
```

`dragRef` and `dropRef` can be merged into one `ref` which can be useful to make whole rows draggable.

<Accordion>
  <AccordionItem title={<InlineCode>DraggableRow</InlineCode>}>

```jsx
import { useDrag, useDrop } from 'react-dnd';
import { useForkRef } from '@gemini-suite/vera-react';

const DraggableRow = React.forwardRef(
  ({ row, reorderRow, children, ...delegated }, forwardedRef) => {
    const [{ isOver }, dropRef] = useDrop({
      accept: 'row',
      drop: draggedRow => reorderRow(draggedRow.id, row.id),
      collect: monitor => ({
        isOver: monitor.isOver()
      })
    });

    const [{ isDragging }, dragRef] = useDrag({
      item: { id: row.id },
      type: 'row',
      collect: monitor => ({
        isDragging: monitor.isDragging()
      })
    });

    const composedRef = useForkRef(forwardedRef, dragRef, dropRef);

    const tableRowStatus = isDragging
      ? 'dragging'
      : isOver
        ? 'dropTarget'
        : undefined;

    return (
      <Table.Row
        ref={composedRef}
        css={{ cursor: 'move', ...css }}
        {...delegated}
        status={tableRowStatus}
      />
    );
  }
);
```

  </AccordionItem>
</Accordion>

```jsx
() => {
  const [data, setData] = React.useState([
    {
      id: 1,
      name: 'Torvald Halvor',
      comment:
        'Shortbread gummies bear claw liquorice liquorice candy chupa chups cheesecake gingerbread.',
      office: 'Oslo'
    },
    {
      id: 2,
      name: 'Mikael Rikard',
      comment:
        'Brownie icing jelly beans macaroon apple pie bear claw candy canes.',
      office: 'Berlin'
    },
    {
      id: 3,
      name: 'Ragnhild Tine',
      comment: 'Gummi bears lollipop biscuit apple pie pudding wafer.',
      office: 'Munich'
    },
    {
      id: 4,
      name: 'Jan Kowalski',
      comment: 'Liquorice candy chupa chups cheesecake.',
      office: 'Warsaw'
    }
  ]);

  const reorderRow = (draggedRowId, targetRowId) => {
    const copy = [...data];
    const firstIndex = copy.findIndex(a => a.id === draggedRowId);
    const secondIndex = copy.findIndex(a => a.id === targetRowId);
    [copy[firstIndex], copy[secondIndex]] = [
      copy[secondIndex],
      copy[firstIndex]
    ];
    setData(copy);
  };

  return (
    <Table.Root aria-label="Draggable rows table">
      <Table.Header>
        <Table.ColumnHeader maxWidth={60}>{'ID'}</Table.ColumnHeader>
        <Table.ColumnHeader>{'Name'}</Table.ColumnHeader>
        <Table.ColumnHeader>{'Comment'}</Table.ColumnHeader>
        <Table.ColumnHeader>{'Office'}</Table.ColumnHeader>
      </Table.Header>
      <Table.Body>
        {data.map(row => (
          <DraggableRow key={row.id} row={row} reorderRow={reorderRow}>
            <Table.Cell maxWidth={60}>{row.id}</Table.Cell>
            <Table.Cell>{row.name}</Table.Cell>
            <Table.Cell>{row.comment}</Table.Cell>
            <Table.Cell>{row.office}</Table.Cell>
          </DraggableRow>
        ))}
      </Table.Body>
    </Table.Root>
  );
};
```

---

## Integration with TanStack Table

**Table** component consists of lightweight, "primitive" building blocks designed for composability.
Primitives are low-level components that assume as little as possible about their usage and they don't own any complex state.

This works well for simple or static tables, but there are instances where functionality such as sorting, search, filtering, [virtualization](https://tanstack.com/virtual/v3) or pagination are required.
This kind of functionality is out of scope for the **Table** component, as the spectrum of possible use-cases is too broad for a single component to cover effectively.
We aim to avoid monolithic components with a lot of assumptions about how you use them and increased API surface area, i.e. endless prop configuration.

Whilst the **Table** component is limited out of the box, these same limitations make it very easy to integrate with third-party libraries which offer advanced functionality.
One such library that we recommend is [TanStack Table](https://tanstack.com/table/v8).
It is a [headless UI library](https://www.merrickchristensen.com/articles/headless-user-interface-components/), which supplies you with functions, state and utilities to add more advanced functionality and interactions to your tables.
It does not bring any user interface or styles itself, making it a perfect extension for Vera's **Table**.

Examples below show how to combine some common [TanStack Table](https://tanstack.com/table/v8) features with Vera's **Table**.

<Callout variant="tip">

To use TanStack Table features, you should install `@tanstack/react-table` package in your project.

```bash
npm install @tanstack/react-table --save
# or
yarn add @tanstack/react-table
```

</Callout>

For a more comprehensive, full-featured demo of a data table built using [TanStack Table](https://tanstack.com/table/v8) and **Table** component, be sure to check out [our Storybook](https://next--698f15fa99e7b146f9002f64.chromatic.com/?path=/story/demos-datatable--default).

<Card
  href="https://next--698f15fa99e7b146f9002f64.chromatic.com/?path=/story/demos-datatable--default"
  title="See data table demo in Storybook"
  actionIcon="externalLink"
/>

### Basic

```js
import {
  flexRender,
  getCoreRowModel,
  useReactTable,
  createColumnHelper
} from '@tanstack/react-table';
```

```jsx
() => {
  const columnHelper = createColumnHelper();
  const columns = React.useMemo(
    () => [
      columnHelper.accessor('id', {
        header: 'ID',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        ),
        meta: {
          width: 120
        }
      }),
      columnHelper.accessor('name', {
        header: 'Name',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        ),
        meta: {
          minWidth: 200
        }
      }),
      columnHelper.accessor('status', {
        header: 'Status',
        cell: ({ getValue }) => {
          const label = getValue();
          return (
            <Flex gap="spacingXs">
              <HintDot tone={label === 'Running' ? 'success' : 'danger'} />
              {label}
            </Flex>
          );
        },
        meta: {
          minWidth: 120,
          maxWidth: 140
        }
      }),
      columnHelper.accessor('host', {
        header: 'Host',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        ),
        meta: {
          minWidth: 180
        }
      }),
      columnHelper.display({
        id: '_actions',
        header: () => <VisuallyHidden>{'Actions'}</VisuallyHidden>,
        cell: ({ row }) => (
          <Menu.Root>
            <Menu.Trigger
              as={Button}
              withLoneIcon
              size="xSmall"
              aria-label="Actions"
              variant="ghost"
            >
              <SvgIcon iconName="more" />
            </Menu.Trigger>
            <Menu.Content anchorOffset={4}>
              <Menu.Item startElement={<SvgIcon iconName="show" />}>
                {'Details'}
              </Menu.Item>
              {row.original.status === 'Running' ? (
                <Menu.Item startElement={<SvgIcon iconName="pause" />}>
                  {'Stop'}
                </Menu.Item>
              ) : (
                <Menu.Item startElement={<SvgIcon iconName="play" />}>
                  {'Start'}
                </Menu.Item>
              )}
            </Menu.Content>
          </Menu.Root>
        ),
        meta: {
          align: 'right',
          width: 60
        }
      })
    ],
    []
  );

  const data = React.useMemo(
    () => [
      {
        id: 'b35ac3bd46',
        name: 'APP1 Production API',
        status: 'Running',
        host: 'testhost:5001'
      },
      {
        id: 'mk35op153l',
        name: 'APP1 Test API',
        status: 'Down',
        host: 'testhost:5002'
      },
      {
        id: 'b6s9dc567e',
        name: 'APP2 Production API',
        status: 'Down',
        host: 'testhost-secondary:5001'
      },
      {
        id: 'z0s93dpl21',
        name: 'APP2 Development API',
        status: 'Running',
        host: 'testhost-secondary:5002'
      },
      {
        id: 'gh6j3s03de',
        name: 'Server Configuration API',
        status: 'Running',
        host: 't3.company.server.configuration:7000'
      }
    ],
    []
  );

  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel()
  });

  return (
    <Table.Root aria-label="Basic example with TanStack Table">
      <Table.Header>
        {table.getFlatHeaders().map(header => (
          <Table.ColumnHeader
            key={header.id}
            width={header.column.columnDef.meta.width}
            minWidth={header.column.columnDef.meta.minWidth}
            maxWidth={header.column.columnDef.meta.maxWidth}
          >
            {flexRender(header.column.columnDef.header, header.getContext())}
          </Table.ColumnHeader>
        ))}
      </Table.Header>
      <Table.Body>
        {table.getRowModel().rows.map(row => (
          <Table.Row key={row.id}>
            {row.getVisibleCells().map(cell => (
              <Table.Cell
                key={cell.id}
                width={cell.column.columnDef.meta.width}
                minWidth={cell.column.columnDef.meta.minWidth}
                maxWidth={cell.column.columnDef.meta.maxWidth}
                align={cell.column.columnDef.meta.align}
              >
                {flexRender(cell.column.columnDef.cell, cell.getContext())}
              </Table.Cell>
            ))}
          </Table.Row>
        ))}
      </Table.Body>
    </Table.Root>
  );
};
```

### Sorting columns

Use [TanStack Table Sorting API](https://tanstack.com/table/v8/docs/api/features/sorting) together with `onClick` and `sort` properties on `Table.ColumnHeader` to render the UI for a sort indicator and allow the user to change the sort order of the data.

<Callout variant="tip">

You should only use client-side sorting if your data set is small and exists in-memory, otherwise sorting should be implemented by sorting in the actual database.

</Callout>

<Callout variant="tip">

Custom sorting icon can be provided with `sortIndicator` attribute. Check different icon by sorting `Name` column below.

</Callout>

<Callout variant="tip">

Table data can be sorted by multiple columns by clicking on a column header while holding down the <Kbd>shift</Kbd> key. This is controlled with the [enableMultiSort property](https://tanstack.com/table/v8/docs/api/features/sorting#enablemultisort-1).

</Callout>

```js
import {
  flexRender,
  getCoreRowModel,
  getSortedRowModel,
  useReactTable,
  createColumnHelper
} from '@tanstack/react-table';
```

```jsx
() => {
  const columnHelper = createColumnHelper();
  const columns = React.useMemo(
    () => [
      columnHelper.accessor('name', {
        header: 'Name',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        )
      }),
      columnHelper.accessor('status', {
        header: 'Status',
        cell: ({ getValue }) => {
          const label = getValue();
          return (
            <Flex gap="spacingXs">
              <HintDot tone={label === 'Running' ? 'success' : 'danger'} />
              {label}
            </Flex>
          );
        },
        meta: {
          maxWidth: 150
        }
      }),
      columnHelper.accessor('host', {
        header: 'Host',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        )
      }),
      columnHelper.accessor('time', {
        header: () => (
          <Table.ColumnHeaderContent>
            <Table.ColumnHeaderTitle>{'Uptime'}</Table.ColumnHeaderTitle>
            <Table.ColumnHeaderSubtitle>{'hours'}</Table.ColumnHeaderSubtitle>
          </Table.ColumnHeaderContent>
        ),
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        ),
        meta: {
          align: 'right',
          maxWidth: 120
        }
      })
    ],
    []
  );

  const data = React.useMemo(
    () => [
      {
        id: 'b35ac3bd46',
        name: 'APP1 Production API',
        status: 'Running',
        host: 'testhost:5001',
        time: '4338'
      },
      {
        id: 'mk35op153l',
        name: 'APP1 Test API',
        status: 'Down',
        host: 'testhost:5002',
        time: '10'
      },
      {
        id: 'b6s9dc567e',
        name: 'APP2 Production API',
        status: 'Down',
        host: 'testhost-secondary:5001',
        time: '51'
      },
      {
        id: 'z0s93dpl21',
        name: 'APP2 Development API',
        status: 'Running',
        host: 'testhost-secondary:5002',
        time: '122'
      },
      {
        id: 'gh6j3s03de',
        name: 'Server Configuration API',
        status: 'Running',
        host: 't3.company.server.configuration:7000',
        time: '2510'
      }
    ],
    []
  );

  const [sorting, setSorting] = React.useState([]);

  const table = useReactTable({
    data,
    columns,
    state: {
      sorting
    },
    onSortingChange: setSorting,
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel()
  });

  return (
    <Table.Root aria-label="Sorting example">
      <Table.Header>
        {table.getFlatHeaders().map(header => (
          <Table.ColumnHeader
            key={header.id}
            onClick={
              header.column.getCanSort()
                ? header.column.getToggleSortingHandler()
                : undefined
            }
            sort={
              header.column.getCanSort()
                ? header.column.getIsSorted() || undefined
                : undefined
            }
            align={header.column.columnDef.meta?.align}
            maxWidth={header.column.columnDef.meta?.maxWidth}
            sortIndicator={
              header.id === 'name' && header.column.getIsSorted() ? (
                <SvgIcon
                  marginLeft="spacingS"
                  iconName={
                    header.column.getIsSorted() === 'desc'
                      ? 'sortAlphaDesc'
                      : 'sortAlphaAsc'
                  }
                />
              ) : undefined
            }
          >
            {flexRender(header.column.columnDef.header, header.getContext())}
          </Table.ColumnHeader>
        ))}
      </Table.Header>
      <Table.Body>
        {table.getRowModel().rows.map(row => (
          <Table.Row key={row.id}>
            {row.getVisibleCells().map(cell => (
              <Table.Cell
                key={cell.id}
                maxWidth={cell.column.columnDef.meta?.maxWidth}
                align={cell.column.columnDef.meta?.align}
              >
                {flexRender(cell.column.columnDef.cell, cell.getContext())}
              </Table.Cell>
            ))}
          </Table.Row>
        ))}
      </Table.Body>
    </Table.Root>
  );
};
```

### Filtering

Filtering data can be implemented with [TanStack Table Filters API](https://tanstack.com/table/v8/docs/api/features/filters).
Table data can be filtered globally with use of `globalFilter` prop or it can be column filtered with `columnFilters`.

```js
import {
  flexRender,
  getCoreRowModel,
  getFilteredRowModel,
  useReactTable,
  createColumnHelper
} from '@tanstack/react-table';
```

```jsx
() => {
  const columnHelper = createColumnHelper();
  const columns = React.useMemo(
    () => [
      columnHelper.accessor('name', {
        header: 'Name',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        )
      }),
      columnHelper.accessor('status', {
        header: 'Status',
        id: 'status',
        enableGlobalFilter: false,
        cell: ({ getValue }) => {
          const label = getValue();
          return (
            <Flex gap="spacingXs">
              <HintDot tone={label === 'Running' ? 'success' : 'danger'} />
              {label}
            </Flex>
          );
        }
      }),
      columnHelper.accessor('host', {
        header: 'Host',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        )
      })
    ],
    []
  );

  const data = React.useMemo(
    () => [
      {
        id: 'b35ac3bd46',
        name: 'APP1 Production API',
        status: 'Running',
        host: 'testhost:5001'
      },
      {
        id: 'mk35op153l',
        name: 'APP1 Test API',
        status: 'Down',
        host: 'testhost:5002'
      },
      {
        id: 'b6s9dc567e',
        name: 'APP2 Production API',
        status: 'Down',
        host: 'testhost-secondary:5001'
      },
      {
        id: 'z0s93dpl21',
        name: 'APP2 Development API',
        status: 'Running',
        host: 'testhost-secondary:5002'
      },
      {
        id: 'gh6j3s03de',
        name: 'Server Configuration API',
        status: 'Running',
        host: 't3.company.server.configuration:7000'
      },
      {
        id: 'h5fhvdfv5f',
        name: 'Services Configuration API',
        status: 'Down',
        host: 't1.company.services.configuration:6000'
      },
      {
        id: 'p0d39c75vl',
        name: 'DB Configuration API',
        status: 'Running',
        host: 't2.company.db.conf:8002'
      }
    ],
    []
  );

  const [filterValue, setFilterValue] = React.useState('');
  const [globalFilter, setGlobalFilter] = React.useState('');
  const [columnFilters, setColumnFilters] = React.useState([]);
  const debouncedFilterValue = useDebouncedValue(filterValue, 300);
  const [isFilterMenuOpen, setIsFilterMenuOpen] = React.useState(false);

  React.useEffect(() => {
    setGlobalFilter(debouncedFilterValue);
  }, [debouncedFilterValue]);

  const table = useReactTable({
    data,
    columns,
    state: {
      columnFilters,
      globalFilter
    },
    onGlobalFilterChange: setGlobalFilter,
    onColumnFiltersChange: setColumnFilters,
    getCoreRowModel: getCoreRowModel(),
    getFilteredRowModel: getFilteredRowModel()
  });

  const rowsSize = table.getCoreRowModel().rows.length;

  const handleResetFilter = () => {
    setFilterValue('');
    setGlobalFilter('');
    setColumnFilters([]);
  };

  return (
    <Flex flow="column">
      <FlexItem as={Flex} cross="center" shrink={0}>
        <FlexItem>
          <TextInput
            aria-label="Search"
            placeholder={rowsSize > 0 ? `Search ${rowsSize} rows` : 'Search'}
            className="maw5"
            type="search"
            leadingVisual={<SvgIcon iconName="search" />}
            value={filterValue}
            onChange={event => setFilterValue(event.target.value)}
          />
        </FlexItem>
        <FlexItem>
          <Menu.Root
            isOpen={isFilterMenuOpen}
            onIsOpenChange={setIsFilterMenuOpen}
          >
            <Menu.Trigger
              as={Button}
              variant="outline"
              rightIcon={
                <SvgIcon
                  iconName={isFilterMenuOpen ? 'chevronUp' : 'chevronDown'}
                />
              }
            >
              {'Filter status'}
              {table.getColumn('status')?.getFilterValue() ? (
                <React.Fragment>
                  {`:`} <b>{table.getColumn('status').getFilterValue()}</b>
                </React.Fragment>
              ) : null}
            </Menu.Trigger>
            <Menu.Content>
              <Menu.RadioGroup
                value={table.getColumn('status')?.getFilterValue() ?? ''}
                onValueChange={value =>
                  table.getColumn('status')?.setFilterValue(value)
                }
              >
                <Menu.RadioItem value="">{'All'}</Menu.RadioItem>
                <Menu.RadioItem value="Running">{'Running'}</Menu.RadioItem>
                <Menu.RadioItem value="Down">{'Down'}</Menu.RadioItem>
              </Menu.RadioGroup>
            </Menu.Content>
          </Menu.Root>
        </FlexItem>
      </FlexItem>
      <FlexItem>
        <Table.Root aria-label="Filtering example">
          <Table.Header>
            {table.getFlatHeaders().map(header => (
              <Table.ColumnHeader key={header.id}>
                {flexRender(
                  header.column.columnDef.header,
                  header.getContext()
                )}
              </Table.ColumnHeader>
            ))}
          </Table.Header>
          {table.getRowModel().rows.length > 0 ? (
            <Table.Body>
              {table.getRowModel().rows.map(row => (
                <Table.Row key={row.id}>
                  {row.getVisibleCells().map(cell => (
                    <Table.Cell key={cell.id}>
                      {flexRender(
                        cell.column.columnDef.cell,
                        cell.getContext()
                      )}
                    </Table.Cell>
                  ))}
                </Table.Row>
              ))}
            </Table.Body>
          ) : (
            <Table.EmptyState>
              <Flex cross="center" flow="column" gap="spacingS">
                <Heading as="h3">{'No matches.'}</Heading>
                <Text
                  as="p"
                  marginTop="none"
                  marginBottom="spacingS"
                  color="foregroundNeutralSubtle"
                >
                  {'We can’t find a match for the provided filter query.'}
                </Text>
                <Button variant="outline" onClick={handleResetFilter}>
                  {'Clear filter'}
                </Button>
              </Flex>
            </Table.EmptyState>
          )}
        </Table.Root>
      </FlexItem>
    </Flex>
  );
};
```

### Selecting rows

Rows selection can be implemented with [Row Selection API](https://tanstack.com/table/v8/docs/api/features/row-selection). In the example below batch actions will be shown upon rows selection.

```js
import {
  flexRender,
  getCoreRowModel,
  useReactTable,
  createColumnHelper
} from '@tanstack/react-table';
```

```jsx
() => {
  const columnHelper = createColumnHelper();
  const columns = React.useMemo(
    () => [
      columnHelper.display({
        id: '_select',
        header: ({ table }) => (
          <Table.SelectionCellCheckbox
            aria-label="Select all"
            isDisabled={table.getRowModel().flatRows.length === 0}
            isChecked={table.getIsAllRowsSelected()}
            isIndeterminate={table.getIsSomeRowsSelected()}
            onChange={() => {
              table.getIsSomeRowsSelected()
                ? table.resetRowSelection()
                : table.toggleAllRowsSelected();
            }}
          />
        ),
        cell: ({ row }) => (
          <Table.SelectionCellCheckbox
            aria-label="Select row"
            isDisabled={!row.getCanSelect()}
            isChecked={row.getIsSelected()}
            isIndeterminate={row.getIsSomeSelected()}
            onChange={row.getToggleSelectedHandler()}
          />
        ),
        meta: {
          width: 40
        }
      }),
      columnHelper.accessor('name', {
        header: 'Name',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        ),
        meta: {
          minWidth: 160
        }
      }),
      columnHelper.accessor('status', {
        header: 'Status',
        cell: ({ getValue }) => {
          const label = getValue();
          return (
            <Flex gap="spacingXs">
              <HintDot tone={label === 'Running' ? 'success' : 'danger'} />
              {label}
            </Flex>
          );
        },
        meta: {
          minWidth: 120,
          maxWidth: 160
        }
      }),
      columnHelper.accessor('host', {
        header: 'Host',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        ),
        meta: {
          minWidth: 200
        }
      })
    ],
    []
  );

  const data = React.useMemo(
    () => [
      {
        id: 'b35ac3bd46',
        name: 'APP1 Production API',
        status: 'Running',
        host: 'testhost:5001'
      },
      {
        id: 'mk35op153l',
        name: 'APP1 Test API',
        status: 'Down',
        host: 'testhost:5002'
      },
      {
        id: 'b6s9dc567e',
        name: 'APP2 Production API',
        status: 'Down',
        host: 'testhost-secondary:5001'
      },
      {
        id: 'z0s93dpl21',
        name: 'APP2 Development API',
        status: 'Running',
        host: 'testhost-secondary:5002'
      },
      {
        id: 'gh6j3s03de',
        name: 'Server Configuration API',
        status: 'Running',
        host: 't3.company.server.configuration:7000'
      }
    ],
    []
  );

  const [rowSelection, setRowSelection] = React.useState({});

  const table = useReactTable({
    data,
    columns,
    state: {
      rowSelection
    },
    onRowSelectionChange: setRowSelection,
    getCoreRowModel: getCoreRowModel()
  });

  const rowsSize = table.getCoreRowModel().rows.length;
  const selectedRowsSize = table.getSelectedRowModel().rows.length;

  return (
    <Flex flow="column">
      <Table.Root aria-label="Selecting rows example">
        <Table.Header stickyOffset={0}>
          {table.getFlatHeaders().map(header => (
            <Table.ColumnHeader
              key={header.id}
              width={header.column.columnDef.meta.width}
              minWidth={header.column.columnDef.meta.minWidth}
              maxWidth={header.column.columnDef.meta.maxWidth}
            >
              {flexRender(header.column.columnDef.header, header.getContext())}
            </Table.ColumnHeader>
          ))}
        </Table.Header>
        <Table.Body>
          {table.getRowModel().rows.map(row => (
            <Table.Row key={row.id} aria-selected={row.getIsSelected()}>
              {row.getVisibleCells().map(cell => (
                <Table.Cell
                  key={cell.id}
                  width={cell.column.columnDef.meta.width}
                  minWidth={cell.column.columnDef.meta.minWidth}
                  maxWidth={cell.column.columnDef.meta.maxWidth}
                >
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </Table.Cell>
              ))}
            </Table.Row>
          ))}
        </Table.Body>
      </Table.Root>
      {selectedRowsSize > 0 && (
        <Flex main="space-between" cross="start">
          {selectedRowsSize} of {rowsSize} row(s) selected.
          <Flex gap="spacingS">
            <Button color="danger" onClick={() => {}}>
              {`Delete selected`}
            </Button>
            <Button
              variant="outline"
              leftIcon={<SvgIcon iconName="upload" />}
              onClick={() => {}}
            >
              {'Export Selected'}
            </Button>
          </Flex>
        </Flex>
      )}
    </Flex>
  );
};
```

### Pagination

With help of [Pagination component](/components/pagination) to display the pagination UI and [TanStack Pagination API](https://tanstack.com/table/v8/docs/api/features/pagination), larger table data sets
can be split into pages.

<Callout variant="tip">

Often pagination is performed server-side and `Table` already receives only data for specific page. In that case [manualPagination property](https://tanstack.com/table/v8/docs/api/features/pagination#manualpagination) is helpful.

</Callout>

<Callout variant="tip">

Be mindful to provide an `aria-description` attribute on the `Table` to let the assistive technologies announce current pagination state.

</Callout>

```js
import {
  flexRender,
  getCoreRowModel,
  getPaginationRowModel,
  useReactTable,
  createColumnHelper
} from '@tanstack/react-table';
```

```jsx
() => {
  const columnHelper = createColumnHelper();
  const columns = React.useMemo(
    () => [
      columnHelper.accessor('name', {
        header: 'Host name',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        ),
        meta: {
          minWidth: 200
        }
      }),
      columnHelper.accessor('ipAddress', {
        header: 'IP Address',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        ),
        meta: {
          minWidth: 140,
          maxWidth: 160
        }
      }),
      columnHelper.accessor('port', {
        header: 'Port',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        ),
        meta: {
          minWidth: 80,
          maxWidth: 100
        }
      }),
      columnHelper.accessor('status', {
        header: 'Status',
        cell: ({ getValue }) => {
          const label = getValue();
          return (
            <Flex gap="spacingXs">
              <HintDot tone={label === 'Running' ? 'success' : 'danger'} />
              {label}
            </Flex>
          );
        },
        meta: {
          minWidth: 120,
          maxWidth: 160
        }
      })
    ],
    []
  );

  const data = React.useMemo(
    () =>
      Array(120)
        .fill()
        .map(() => ({
          name: faker.internet.domainName(),
          ipAddress: faker.internet.ip(),
          port: faker.internet.port(),
          status: faker.helpers.arrayElement(['Running', 'Down'])
        })),
    []
  );

  const [{ pageIndex, pageSize }, setPagination] = React.useState({
    pageIndex: 0,
    pageSize: 10
  });

  const pagination = React.useMemo(
    () => ({
      pageIndex,
      pageSize
    }),
    [pageIndex, pageSize]
  );

  const table = useReactTable({
    data,
    state: {
      pagination
    },
    pageCount: Math.ceil(data.length / pageSize),
    onPaginationChange: setPagination,
    columns,
    getCoreRowModel: getCoreRowModel(),
    getPaginationRowModel: getPaginationRowModel()
  });

  const pageCount = table.getPageCount();

  const paginationRange = usePagination({
    currentPage: pageIndex,
    pageCount: table.getPageCount()
  });

  const pageSizeOptions = [10, 25, 50].map(s => ({
    value: s.toString(),
    label: s.toString()
  }));

  const pageCursorStart = pageIndex * pageSize + 1;
  const pageCursorEnd = Math.min(pageCursorStart + pageSize - 1, data.length);

  return (
    <Flex flow="column">
      <Table.Root
        syncScroll
        aria-label="Pagination example"
        aria-description={`Page ${pageIndex + 1} of ${pageCount}`}
      >
        <Table.Header stickyOffset={0}>
          {table.getFlatHeaders().map(header => (
            <Table.ColumnHeader
              key={header.id}
              width={header.column.columnDef.meta.width}
              minWidth={header.column.columnDef.meta.minWidth}
              maxWidth={header.column.columnDef.meta.maxWidth}
            >
              {flexRender(header.column.columnDef.header, header.getContext())}
            </Table.ColumnHeader>
          ))}
        </Table.Header>
        <Table.Body>
          {table.getRowModel().rows.map(row => (
            <Table.Row key={row.id}>
              {row.getVisibleCells().map(cell => (
                <Table.Cell
                  key={cell.id}
                  width={cell.column.columnDef.meta.width}
                  minWidth={cell.column.columnDef.meta.minWidth}
                  maxWidth={cell.column.columnDef.meta.maxWidth}
                >
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </Table.Cell>
              ))}
            </Table.Row>
          ))}
        </Table.Body>
      </Table.Root>
      <Flex main="space-between" cross="start">
        <Pagination.Root
          as={Flex}
          wrap="wrap"
          gap="spacingS"
          label="Pagination navigation"
        >
          <Pagination.Arrow
            label="Go to previous page"
            direction="back"
            onClick={() => table.previousPage()}
            isDisabled={!table.getCanPreviousPage()}
          />
          <Pagination.Numbers className="is-visibleLarge">
            {paginationRange.map((pageNumber, index) => {
              if (pageNumber === -1) {
                return (
                  <Pagination.Ellipsis
                    key={`dots-${index}`}
                    label="Collapsed pages"
                  />
                );
              }

              return (
                <Pagination.Number
                  key={pageNumber}
                  label={`Go to page ${pageNumber}`}
                  isCurrent={pageNumber === pageIndex + 1}
                  onClick={() => table.setPageIndex(pageNumber - 1)}
                >
                  {pageNumber}
                </Pagination.Number>
              );
            })}
          </Pagination.Numbers>
          <Pagination.Arrow
            label="Go to next page"
            direction="forward"
            onClick={() => table.nextPage()}
            disabled={!table.getCanNextPage()}
          />
          <Pagination.Label>
            {`${pageCursorStart}–${pageCursorEnd} of ${data.length}`}
          </Pagination.Label>
        </Pagination.Root>
        <FlexItem shrink={0}>
          <FormField.Root as={Flex} cross="center">
            <FormField.Label>{'Show'}</FormField.Label>
            <Select
              css={{ maxWidth: '9ch' }}
              placeholder="Items"
              onChange={value => table.setPageSize(Number(value))}
              options={pageSizeOptions}
              value={pageSize}
            />
          </FormField.Root>
        </FlexItem>
      </Flex>
    </Flex>
  );
};
```

### Resizable columns

Resizing columns can be implemented with [TanStack Column Sizing API](https://tanstack.com/table/v8/docs/api/features/column-sizing).

Pass `Table.ColumnResizeHandle` to `Table.ColumnHeader` via the `resizeHandle` prop to enable column resizing. This component renders a draggable handle that users interact with to adjust column width. Connect it to TanStack Table by:

- Passing `header.getResizeHandler()` to `onMouseDown` and `onTouchStart` props
- Passing `header.column.getIsResizing()` to `isResizing` for visual feedback

You can control initial column size using the `size` property on a column definition. You can also constrain
resizing with `minSize` and `maxSize` properties.

<Callout variant="tip">

Maintain flex behaviour of table with resizable columns by using `flexBasis` prop instead of `width`. This allows the column to grow and shrink fluidly.

</Callout>

<Card
  href="https://next--698f15fa99e7b146f9002f64.chromatic.com/?path=/story/components-table--resizable-columns-fit-content"
  title="See more examples in our Storybook"
  actionIcon="externalLink"
/>

```js
import {
  flexRender,
  getCoreRowModel,
  getSortedRowModel,
  useReactTable,
  createColumnHelper
} from '@tanstack/react-table';
```

```jsx
() => {
  const columnHelper = createColumnHelper();
  const columns = React.useMemo(
    () => [
      columnHelper.accessor('name', {
        header: 'Name',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        )
      }),
      columnHelper.accessor('status', {
        header: 'Status',
        cell: ({ getValue }) => {
          const label = getValue();
          return (
            <Flex gap="spacingXs">
              <HintDot tone={label === 'Running' ? 'success' : 'danger'} />
              {label}
            </Flex>
          );
        }
      }),
      columnHelper.accessor('host', {
        header: 'Host',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        )
      }),
      columnHelper.accessor('time', {
        header: () => (
          <Table.ColumnHeaderContent>
            <Table.ColumnHeaderTitle>{'Uptime'}</Table.ColumnHeaderTitle>
            <Table.ColumnHeaderSubtitle>{'hours'}</Table.ColumnHeaderSubtitle>
          </Table.ColumnHeaderContent>
        ),
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        )
      })
    ],
    []
  );

  const data = React.useMemo(
    () => [
      {
        id: 'b35ac3bd46',
        name: 'APP1 Production API',
        status: 'Running',
        host: 'testhost:5001',
        time: '4338'
      },
      {
        id: 'mk35op153l',
        name: 'APP1 Test API',
        status: 'Down',
        host: 'testhost:5002',
        time: '10'
      },
      {
        id: 'b6s9dc567e',
        name: 'APP2 Production API',
        status: 'Down',
        host: 'testhost-secondary:5001',
        time: '51'
      },
      {
        id: 'z0s93dpl21',
        name: 'APP2 Development API',
        status: 'Running',
        host: 'testhost-secondary:5002',
        time: '122'
      },
      {
        id: 'gh6j3s03de',
        name: 'Server Configuration API',
        status: 'Running',
        host: 't3.company.server.configuration:7000',
        time: '2510'
      }
    ],
    []
  );

  const [sorting, setSorting] = React.useState([]);

  const table = useReactTable({
    data,
    columns,
    state: {
      sorting
    },
    columnResizeMode: 'onChange',
    defaultColumn: {
      minSize: 100,
      size: 160,
      maxSize: 400
    },
    onSortingChange: setSorting,
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel()
  });

  return (
    <Table.Root aria-label="Resizable columns example">
      <Table.Header>
        {table.getFlatHeaders().map(header => (
          <Table.ColumnHeader
            key={header.id}
            flexBasis={header.column.getSize()}
            onClick={
              header.column.getCanSort()
                ? header.column.getToggleSortingHandler()
                : undefined
            }
            sort={
              header.column.getCanSort()
                ? header.column.getIsSorted() || undefined
                : undefined
            }
            resizeHandle={
              header.column.getCanResize() ? (
                <Table.ColumnResizeHandle
                  onMouseDown={header.getResizeHandler()}
                  onTouchStart={header.getResizeHandler()}
                  onDoubleClick={() => header.column.resetSize()}
                  isResizing={header.column.getIsResizing()}
                />
              ) : undefined
            }
          >
            {flexRender(header.column.columnDef.header, header.getContext())}
          </Table.ColumnHeader>
        ))}
      </Table.Header>
      <Table.Body>
        {table.getRowModel().rows.map(row => (
          <Table.Row key={row.id}>
            {row.getVisibleCells().map(cell => (
              <Table.Cell key={cell.id} flexBasis={cell.column.getSize()}>
                {flexRender(cell.column.columnDef.cell, cell.getContext())}
              </Table.Cell>
            ))}
          </Table.Row>
        ))}
      </Table.Body>
    </Table.Root>
  );
};
```

### Columns visibility

Toggling columns visibility can be implemented with [Visibility API](https://tanstack.com/table/v8/docs/api/features/column-visibility).

```js
import {
  flexRender,
  getCoreRowModel,
  useReactTable,
  createColumnHelper
} from '@tanstack/react-table';
```

```jsx
() => {
  const columnHelper = createColumnHelper();
  const columns = React.useMemo(
    () => [
      columnHelper.accessor('name', {
        header: 'Name',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        )
      }),
      columnHelper.accessor('status', {
        header: 'Status',
        id: 'status',
        enableGlobalFilter: false,
        cell: ({ getValue }) => {
          const label = getValue();
          return (
            <Flex gap="spacingXs">
              <HintDot tone={label === 'Running' ? 'success' : 'danger'} />
              {label}
            </Flex>
          );
        }
      }),
      columnHelper.accessor('host', {
        header: 'Host',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        )
      })
    ],
    []
  );

  const data = React.useMemo(
    () => [
      {
        id: 'b35ac3bd46',
        name: 'APP1 Production API',
        status: 'Running',
        host: 'testhost:5001'
      },
      {
        id: 'mk35op153l',
        name: 'APP1 Test API',
        status: 'Down',
        host: 'testhost:5002'
      },
      {
        id: 'b6s9dc567e',
        name: 'APP2 Production API',
        status: 'Down',
        host: 'testhost-secondary:5001'
      },
      {
        id: 'z0s93dpl21',
        name: 'APP2 Development API',
        status: 'Running',
        host: 'testhost-secondary:5002'
      },
      {
        id: 'gh6j3s03de',
        name: 'Server Configuration API',
        status: 'Running',
        host: 't3.company.server.configuration:7000'
      }
    ],
    []
  );

  const [columnVisibility, setColumnVisibility] = React.useState({});

  const table = useReactTable({
    data,
    columns,
    state: {
      columnVisibility
    },
    onColumnVisibilityChange: setColumnVisibility,
    getCoreRowModel: getCoreRowModel()
  });

  const visibleColumnsLength = table.getVisibleFlatColumns().length;

  return (
    <Flex flow="column">
      <FlexItem as={Flex} main="end" shrink={0}>
        <Menu.Root>
          <Menu.Trigger as={Button} variant="outline" rightIcon={<Menu.Icon />}>
            {'Columns'}
          </Menu.Trigger>
          <Menu.Content placement="bottom_right">
            {table
              .getAllColumns()
              .filter(
                column =>
                  typeof column.accessorFn !== 'undefined' &&
                  column.getCanHide()
              )
              .map(column => {
                const isVisible = column.getIsVisible();

                return (
                  <Menu.CheckboxItem
                    key={column.id}
                    isChecked={isVisible}
                    onIsCheckedChange={column.toggleVisibility}
                    isDisabled={visibleColumnsLength === 1 && isVisible}
                  >
                    {column.columnDef.header
                      ? String(column.columnDef.header)
                      : column.id}
                  </Menu.CheckboxItem>
                );
              })}
          </Menu.Content>
        </Menu.Root>
      </FlexItem>
      <Table.Root aria-label="Columns visibility example">
        <Table.Header stickyOffset={0}>
          {table.getFlatHeaders().map(header => (
            <Table.ColumnHeader key={header.id}>
              {flexRender(header.column.columnDef.header, header.getContext())}
            </Table.ColumnHeader>
          ))}
        </Table.Header>
        <Table.Body>
          {table.getRowModel().rows.map(row => (
            <Table.Row key={row.id}>
              {row.getVisibleCells().map(cell => (
                <Table.Cell key={cell.id}>
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </Table.Cell>
              ))}
            </Table.Row>
          ))}
        </Table.Body>
      </Table.Root>
    </Flex>
  );
};
```

### Rows virtualization

Rendering large amounts of rows can be inefficient.
With [virtualization](https://tanstack.com/virtual/v3) (or windowing) of data, only currently visible rows are rendered to DOM to deliver best performance and user experience.

<Callout variant="tip">

When only a subset of rows are visible, it's a good practice to let all users know which
rows are being displayed.

Use the `aria-rowcount` attribute on the `Table` to let assistive technologies know the total number of rows available. `Table.Row` should include `aria-rowindex` attribute to indicate where each row is in relation to the total available rows.

</Callout>

<Callout variant="important">

Virtualization achieves best performance with a fixed row height, so it's not recommended to combine it with `overflowStrategy="wrap"`.

</Callout>

```js
import {
  flexRender,
  getCoreRowModel,
  useReactTable,
  createColumnHelper
} from '@tanstack/react-table';
import { useVirtualizer } from '@tanstack/react-virtual';
```

```jsx
() => {
  const columnHelper = createColumnHelper();
  const columns = React.useMemo(
    () => [
      columnHelper.accessor('name', {
        header: 'Host name',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        ),
        meta: {
          minWidth: 200
        }
      }),
      columnHelper.accessor('ipAddress', {
        header: 'IP Address',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        ),
        meta: {
          minWidth: 140,
          maxWidth: 160
        }
      }),
      columnHelper.accessor('port', {
        header: 'Port',
        cell: ({ getValue }) => (
          <Table.CellContent>{getValue()}</Table.CellContent>
        ),
        meta: {
          minWidth: 80,
          maxWidth: 100
        }
      }),
      columnHelper.accessor('status', {
        header: 'Status',
        cell: ({ getValue }) => {
          const label = getValue();
          return (
            <Flex gap="spacingXs">
              <HintDot tone={label === 'Running' ? 'success' : 'danger'} />
              {label}
            </Flex>
          );
        },
        meta: {
          minWidth: 120,
          maxWidth: 160
        }
      })
    ],
    []
  );

  const data = React.useMemo(
    () =>
      Array(5000)
        .fill()
        .map(() => ({
          name: faker.internet.domainName(),
          ipAddress: faker.internet.ip(),
          port: faker.internet.port(),
          status: faker.helpers.arrayElement(['Running', 'Down'])
        })),
    []
  );

  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel()
  });

  const scrollContainer = React.useRef(null);

  const rowVirtualizer = useVirtualizer({
    count: data.length,
    getScrollElement: () => scrollContainer.current,
    overscan: 5,
    estimateSize: () => 41
  });
  const { getTotalSize, getVirtualItems } = rowVirtualizer;

  const virtualRows = getVirtualItems();
  const tableRows = table.getRowModel().rows;

  const paddingTop = virtualRows.length > 0 ? virtualRows[0].start : 0;
  const paddingBottom =
    virtualRows.length > 0
      ? getTotalSize() - virtualRows[virtualRows.length - 1].end
      : 0;

  return (
    <Table.Root
      css={{ height: 400 }}
      ref={scrollContainer}
      aria-rowcount={tableRows.length + 1}
    >
      <Table.Head stickyOffset={0}>
        <Table.Row aria-rowindex={1}>
          {table.getFlatHeaders().map(header => (
            <Table.ColumnHeader
              key={header.id}
              width={header.column.columnDef.meta.width}
              minWidth={header.column.columnDef.meta.minWidth}
              maxWidth={header.column.columnDef.meta.maxWidth}
            >
              {flexRender(header.column.columnDef.header, header.getContext())}
            </Table.ColumnHeader>
          ))}
        </Table.Row>
      </Table.Head>
      <Table.Body>
        {paddingTop > 0 ? (
          <div
            aria-hidden="true"
            key="offset-top"
            style={{ height: paddingTop }}
          />
        ) : null}
        {virtualRows.map(virtualRow => {
          const row = tableRows[virtualRow.index];

          return (
            <Table.Row key={row.id} aria-rowindex={virtualRow.index + 2}>
              {row.getVisibleCells().map(cell => (
                <Table.Cell
                  key={cell.id}
                  width={cell.column.columnDef.meta.width}
                  minWidth={cell.column.columnDef.meta.minWidth}
                  maxWidth={cell.column.columnDef.meta.maxWidth}
                >
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </Table.Cell>
              ))}
            </Table.Row>
          );
        })}
        {paddingBottom > 0 ? (
          <div
            aria-hidden="true"
            key="offset-bottom"
            style={{
              height: paddingBottom
            }}
          />
        ) : null}
      </Table.Body>
    </Table.Root>
  );
};
```

---

## API Reference

### Table.Root

| Name                      | Type                            | Default      | Description                                                                                                                                                                                                                                                 | Required |
| ------------------------- | ------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`                     | `StitchesCss`                   |              | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants.                |          |
| `alignY`                  | `"center" \| "bottom" \| "top"` | `"center"`   | Vertical alignment of the content within table cells.                                                                                                                                                                                                       |          |
| `cellSize`                | `"small" \| "medium"`           | `"medium"`   | Controls the size of the table's cells. `small` size helps to conserve space.                                                                                                                                                                               |          |
| `overflowStrategy`        | `"wrap" \| "truncate"`          | `"truncate"` | Controls the overflow behavior for the text content of each cell.                                                                                                                                                                                           |          |
| `highlightOnHover`        | `boolean`                       | `true`       | When `true`, rows will have hover color.                                                                                                                                                                                                                    |          |
| `syncScroll`              | `boolean`                       | `false`      | When `true`, table's row groups become scrolling containers instead of the `Table` and their scrolling position is synced up. Useful when you want to make `Table.Head` or `Table.Footer` "sticky" relative to the window as the user scrolls a long table. |          |
| `aria-label`              | `string`                        |              | Used to provide a better description of the table for users with assistive technologies.                                                                                                                                                                    | Yes      |
| `rowSeparation`           | `"dividers" \| "stripes"`       | `"dividers"` | Defines the appearance of row separation to aid users to distinguish rows.                                                                                                                                                                                  |          |
| `outlined`                | `boolean`                       | `true`       | Enable or disable table's outline.                                                                                                                                                                                                                          |          |
| `addHorizontalWhitespace` | `boolean`                       | `false`      | When `true`, each first and last cell in a row gets additional left and right padding applied, equal to the whitespace between adjacent cells.                                                                                                              |          |
| `showCellSeparation`      | `boolean`                       | `false`      | When `true`, there is a visible separation between table cells. Useful when using `rowSeparation="stripes"` together with `tone` on rows or cells.                                                                                                          |          |
| `fitContent`              | `boolean`                       | `false`      | When `true`, the table width is set to `fit-content`, allowing the table to shrink to fit its content instead of filling the available width.                                                                                                               |          |

### Table.Head

| Name           | Type          | Default | Description                                                                                                                                                                                                                                  | Required |
| -------------- | ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`          | `StitchesCss` |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants. |          |
| `stickyOffset` | `number`      |         | Applies "sticky" positioning to the `Table.Head` with a defined offset against the top. When `scrollSync` is `true`, the offset is relative to nearest scrolling ancestor of the `Table`.                                                    |          |

### Table.Header

| Name           | Type          | Default | Description                                                                                                                                                                                                                                  | Required |
| -------------- | ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`          | `StitchesCss` |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants. |          |
| `stickyOffset` | `number`      |         | Applies "sticky" positioning to the `Table.Head` with a defined offset against the top. When `scrollSync` is `true`, the offset is relative to nearest scrolling ancestor of the `Table`.                                                    |          |

### Table.ColumnHeader

| Name            | Type                                      | Default  | Description                                                                                                                                                                                                                                  | Required |
| --------------- | ----------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`           | `StitchesCss`                             |          | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants. |          |
| `align`         | `"center" \| "left" \| "right"`           | `"left"` | Text alignment of the content within the header cell.                                                                                                                                                                                        |          |
| `maxWidth`      | `CSSProperties['maxWidth']`               |          | Maximum width of the header cell.                                                                                                                                                                                                            |          |
| `minWidth`      | `CSSProperties['minWidth']`               |          | Minimum width of the header cell.                                                                                                                                                                                                            |          |
| `width`         | `CSSProperties['width']`                  |          | Width of the header cell.                                                                                                                                                                                                                    |          |
| `flexBasis`     | `CSSProperties['flexBasis']`              |          | Sets the initial size of the header cell along the main axis. Useful for controlling the flex layout behavior when cells need custom sizing.                                                                                                 |          |
| `stickyOffset`  | `number`                                  |          | Applies "sticky" positioning to the `Table.ColumnHeader` with a defined offset. Positive values will offset the cell to the left. Negative values will offset the cell to the right.                                                         |          |
| `showDivider`   | `boolean`                                 |          | When `true`, the header cell renders a divider between it and the next header.                                                                                                                                                               |          |
| `onClick`       | `React.MouseEventHandler<HTMLDivElement>` |          | Event handler called when a user clicks the column header. Typically used to handle sorting.                                                                                                                                                 |          |
| `sort`          | `"asc" \| "desc"`                         |          | Defines column sorting direction. Indicates if items in a column are sorted in ascending or descending order. Sets `aria-sort` attribute.                                                                                                    |          |
| `sortIndicator` | `React.ReactElement`                      |          | `ReactElement` to render next to `Table.ColumnHeader` content as sorting indicator.                                                                                                                                                          |          |
| `resizeHandle`  | `React.ReactElement`                      |          | `ReactElement` to render as a column resize handle, typically a `Table.ColumnResizeHandle`.                                                                                                                                                  |          |
| `isGroupHeader` | `boolean`                                 | `false`  | When `true`, reduces vertical padding for use in group header rows that span multiple columns.                                                                                                                                               |          |

### Table.ColumnHeaderContent

| Name               | Type                                                      | Default | Description                                                                                                                                                                                                                                                                                  | Required |
| ------------------ | --------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`               | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `div`   | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](/get-started/composition#polymorphism) guide. |          |
| `css`              | `StitchesCss`                                             |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants.                                                 |          |
| `overflowStrategy` | `"wrap" \| "truncate"`                                    |         | Controls the overflow behavior for the text content of the cell.                                                                                                                                                                                                                             |          |

### Table.ColumnHeaderTitle

| Name               | Type                                                      | Default | Description                                                                                                                                                                                                                                                                                  | Required |
| ------------------ | --------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`               | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `span`  | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](/get-started/composition#polymorphism) guide. |          |
| `css`              | `StitchesCss`                                             |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants.                                                 |          |
| `overflowStrategy` | `"wrap" \| "truncate"`                                    |         | Controls the overflow behavior for the text content of headers name.                                                                                                                                                                                                                         |          |

### Table.ColumnHeaderSubtitle

| Name               | Type                                                      | Default | Description                                                                                                                                                                                                                                                                                  | Required |
| ------------------ | --------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`               | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `span`  | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](/get-started/composition#polymorphism) guide. |          |
| `css`              | `StitchesCss`                                             |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants.                                                 |          |
| `overflowStrategy` | `"wrap" \| "truncate"`                                    |         | Controls the overflow behavior for the text content of headers unit.                                                                                                                                                                                                                         |          |

### Table.ColumnResizeHandle

| Name         | Type          | Default | Description                                                                                                                                                                                                                                  | Required |
| ------------ | ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`        | `StitchesCss` |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants. |          |
| `isResizing` | `boolean`     | `false` | When `true`, the handle is in an active resizing state. Used to provide visual feedback during column resize operations.                                                                                                                     |          |

### Table.SortIndicator

| Name  | Type          | Default | Description                                                                                                                                                                                                                                  | Required |
| ----- | ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css` | `StitchesCss` |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants. |          |

### Table.Body

| Name  | Type          | Default | Description                                                                                                                                                                                                                                  | Required |
| ----- | ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css` | `StitchesCss` |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants. |          |

### Table.Row

| Name            | Type                                             | Default | Description                                                                                                                                                                                                                                  | Required |
| --------------- | ------------------------------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`           | `StitchesCss`                                    |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants. |          |
| `status`        | `"dragging" \| "dropTarget"`                     |         | Row `status` affecting it's visual style.                                                                                                                                                                                                    |          |
| `isHighlighted` | `boolean`                                        | `false` | When `true`, the row is visually highlighted.                                                                                                                                                                                                |          |
| `tone`          | `"danger" \| "success" \| "warning" \| "custom"` |         | Sets the status of the row.                                                                                                                                                                                                                  |          |

### Table.Cell

| Name           | Type                                             | Default      | Description                                                                                                                                                                                                                                  | Required |
| -------------- | ------------------------------------------------ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`          | `StitchesCss`                                    |              | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants. |          |
| `align`        | `"center" \| "left" \| "right"`                  | `"left"`     | Text alignment of the content within the cell.                                                                                                                                                                                               |          |
| `maxWidth`     | `CSSProperties['maxWidth']`                      |              | Maximum width of the cell.                                                                                                                                                                                                                   |          |
| `minWidth`     | `CSSProperties['minWidth']`                      |              | Minimum width of the cell.                                                                                                                                                                                                                   |          |
| `width`        | `CSSProperties['width']`                         |              | Width of the cell.                                                                                                                                                                                                                           |          |
| `flexBasis`    | `CSSProperties['flexBasis']`                     |              | Sets the initial size of the cell along the main axis. Useful for controlling the flex layout behavior when cells need custom sizing.                                                                                                        |          |
| `stickyOffset` | `number`                                         |              | Applies "sticky" positioning to the `Table.Cell` with a defined offset. Positive values will offset the cell to the left. Negative values will offset the cell to the right.                                                                 |          |
| `showDivider`  | `boolean`                                        |              | When `true`, the cell renders a divider between it and the next cell.                                                                                                                                                                        |          |
| `role`         | `"gridcell" \| "rowheader" \| "columnheader"`    | `"gridcell"` | [ARIA Role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles) of the cell.                                                                                                                                              |          |
| `tone`         | `"danger" \| "success" \| "warning" \| "custom"` |              | Sets the status of the cell.                                                                                                                                                                                                                 |          |

### Table.CellContent

<Callout variant="tip">

In addition to the props below, you can pass [Text props](/components/typography/text#api-reference).

</Callout>

| Name               | Type                                                      | Default | Description                                                                                                                                                                                                                                                                                  | Required |
| ------------------ | --------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`               | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `div`   | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](/get-started/composition#polymorphism) guide. |          |
| `css`              | `StitchesCss`                                             |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants.                                                 |          |
| `overflowStrategy` | `"wrap" \| "truncate"`                                    |         | Controls the overflow behavior for the text content of the cell.                                                                                                                                                                                                                             |          |

### Table.SelectionCell

<Callout variant="tip">

In addition to the props below, you can pass all [Table.SelectionCellCheckbox props](#tableselectioncellcheckbox).

</Callout>

| Name           | Type                                          | Default    | Description                                                                                                                                                                                                                                  | Required |
| -------------- | --------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`          | `StitchesCss`                                 |            | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants. |          |
| `align`        | `"center" \| "left" \| "right"`               | `"left"`   | Text alignment of the content within the cell.                                                                                                                                                                                               |          |
| `width`        | `CSSProperties['width']`                      |            | Width of the selection cell.                                                                                                                                                                                                                 |          |
| `stickyOffset` | `number`                                      |            | Applies "sticky" positioning to the `Table.SelectionCell` with a defined offset. Positive values will offset the cell to the left. Negative values will offset the cell to the right.                                                        |          |
| `showDivider`  | `boolean`                                     |            | When `true`, the selection cell renders a divider between it and the next cell.                                                                                                                                                              |          |
| `role`         | `"gridcell" \| "rowheader" \| "columnheader"` | `gridcell` | [ARIA Role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles) of the selection cell.                                                                                                                                    |          |
| `aria-label`   | `string`                                      |            | Used to provide a better description of the checkbox control for users with assistive technologies.                                                                                                                                          | Yes      |

### Table.SelectionCellCheckbox

<Callout variant="tip">

In addition to the props below, you can pass all [Checkbox props](/components/forms/checkbox#checkbox).

</Callout>

| Name         | Type          | Default | Description                                                                                                                                                                                                                                  | Required |
| ------------ | ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`        | `StitchesCss` |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants. |          |
| `aria-label` | `string`      |         | Used to provide a better description of the checkbox control for users with assistive technologies.                                                                                                                                          | Yes      |

### Table.Foot

| Name           | Type          | Default | Description                                                                                                                                                                                                                                  | Required |
| -------------- | ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`          | `StitchesCss` |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants. |          |
| `stickyOffset` | `number`      |         | Applies "sticky" positioning to the `Table.Foot` with a defined offset against the bottom. When `scrollSync` is `true`, the offset is relative to nearest scrolling ancestor of the `Table`.                                                 |          |

### Table.Footer

| Name           | Type          | Default | Description                                                                                                                                                                                                                                  | Required |
| -------------- | ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`          | `StitchesCss` |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants. |          |
| `stickyOffset` | `number`      |         | Applies "sticky" positioning to the `Table.Foot` with a defined offset against the bottom. When `scrollSync` is `true`, the offset is relative to nearest scrolling ancestor of the `Table`.                                                 |          |

### Table.EmptyState

| Name  | Type          | Default | Description                                                                                                                                                                                                                                  | Required |
| ----- | ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css` | `StitchesCss` |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants. |          |
