Table is a set of structured data that is easy for a user to scan, examine, and compare. Table data is displayed in a grid format and can be used to structure both interactive and static data.

also known as Data Table, Data Grid

Responsive:

Adaptive:

Props

Component props
Name
Type
Default
accessibilityLabel
Required
string
-

Label for screen readers to announce Table.

children
Required
React.ChildrenArray<ReactElement>
-

Must be instances of Table.Header, Table.Body, and/or Table.Footer components. See the Subcomponent section to learn more.

borderStyle
"sm" | "none"
-

Specify a border width for Table: "sm" is 1px.

maxHeight
number | string
-

Use numbers for pixels: maxHeight={100} and strings for percentages: maxHeight="100%".

stickyColumns
number | null | undefined
-

Specify how many columns from the start of the Table should be sticky when scrolling horizontally. See the sticky column, multiple sticky columns, sticky header and columns, expandable row with sticky columns, and sortable header cells with sticky columns variants for details.

Usage guidelines

When to use
  • Displaying a set of structured data in a scannable way, that populates 2 or more rows.
  • Allowing users to compare information in rows and columns.
When not to use
  • There will never be enough data to populate at least 2 rows.
  • Displaying content that doesn’t follow a consistent pattern and can't be broken down into columns.
  • Providing robust data that doesn't fit in a tabular format. If there is a need to display a more complex data relationship, consider an info-graphic or a non-tabular format.

Best practices

Style

Do

Use accessible Gestalt grays for table text, and reserve colors to sparingly accent important status and information. Avoid over-styling text.

Don't

Overuse color and styling for text in tables; it can make it hard to scan for important status updates and crucial information.

Alignment

Do

Align content so that it’s easy to scan, read and compare:

  • Start-align text and combo-content (combinations of text, numbers and/or graphics)
  • End-align numbers only
  • Align headers with their corresponding content
  • Use tabular lining for numbers
Don't

Align content so that it makes it harder to scan, read, and compare.

  • Center-align content
  • Use proportional figures for numbers as they don’t quite align
  • End-align text and combo-content (combinations of text, numbers and/or graphics)
  • Misalign headers with their corresponding content
Do

Place unit type on a separate column so that amounts can still align and be compared.

Don't

Mix text and graphics with numbers that need to be compared with each other.

Content

Do

Make content digestible and scannable:

  • Keep headers clear and concise
  • Include an a visual indicator for cells that don’t have content.
  • Give enough space for content to account for localization.
  • Wrap important content to multiple lines
  • Truncate secondary information, especially if a user is going to get the full content upon click of a link in the table.
Don't

Add so much content that it’s hard for a user to read, examine and scan:

  • Don’t truncate content that a user needs to examine in relation to other content in the table.
  • Leave cells blank so that it isn’t clear if all data has loaded.
Do

Expand rows if the additional content is simple, doesn’t contain a lot of interaction and doesn’t take up more than 50% of the screen.

Don't

Use an expand to display dense, highly-interactive content. Use a new page or OverlayPanel for that.

Accessibility

Labels

Use accessibilityLabel to properly announce the content of the table. For example, use "Campaign Status Information".

Don’t include the word “table” as part of the label to prevent redundancy: the VoiceOver already appends “table” to the label and the Category “Table” in the rotor already describes the nature of the component.

In terms of structure and content, HTML tables already provide accessible ways to navigate content via cells <td> and headers <th>.

Keyboard navigation

The Tab key should only place the focus on interactive elements like sortable headers, expands and links. If a cell does not contain interactive content, tabbing should skip the cell. Enter, Space and Return activate buttons and other controls after focusing. Arrow keys can be used to scroll table content vertically and horizontally.

Other considerations

Internally, Gestalt Table implements visually-hidden captions through the accessibilityLabel prop. Therefore, if we want to add visual captions (at the top or bottom of the Table), we must prevent redundancy. Any top or bottom text that describes the Table should be removed from navigation using aria-hidden.

See the examples below for more details.

import { Box, Checkbox, Flex, Label, Status, Table, Text } from 'gestalt';

function HeaderRow({ id }) {
  return (
    <Table.Header>
      <Table.Row>
        <Table.HeaderCell>
          <Box display="visuallyHidden">
            <Label htmlFor={id}>Not all checkboxes are checked</Label>
          </Box>
          <Checkbox id={id} indeterminate onChange={() => {}} size="sm" />
        </Table.HeaderCell>
        {['Status', 'Campaign'].map((title) => (
          <Table.HeaderCell key={title}>
            <Text weight="bold">{title}</Text>
          </Table.HeaderCell>
        ))}
      </Table.Row>
    </Table.Header>
  );
}

function BaseRow({ id, checked, disabled, type, text, subtext, campaign }) {
  return (
    <Table.Row>
      <Table.Cell>
        <Checkbox
          checked={checked}
          disabled={disabled}
          id={`${id.replace(/ /g, '_').replace(/'/g, '')}_${text
            .replace(/ /g, '_')
            .replace(/'/g, '')}`}
          onChange={() => {}}
          size="sm"
        />
      </Table.Cell>
      <Table.Cell>
        <Status subtext={subtext} title={text} type={type} />
      </Table.Cell>
      <Table.Cell>
        <Label
          htmlFor={`${id.replace(/ /g, '_').replace(/'/g, '')}_${text
            .replace(/ /g, '_')
            .replace(/'/g, '')}`}
        >
          <Text color={disabled ? 'subtle' : 'default'}>{campaign}</Text>
        </Label>
      </Table.Cell>
    </Table.Row>
  );
}

export default function Example() {
  const tableID = 'Example of correct accessibility with top caption';

  return (
    <Box padding={4}>
      <Flex direction="column" gap={{ column: 2, row: 0 }}>
        <Box aria-hidden>
          <Text size="400" weight="bold">
            Your Campaigns Summary
          </Text>
        </Box>
        <Table accessibilityLabel="Your campaigns summary">
          <HeaderRow id={tableID} />
          <Table.Body>
            <BaseRow
              campaign="Engagement"
              checked
              id={tableID}
              subtext="Ends 11/20/2021"
              text="In progress"
              type="inProgress"
            />
            <BaseRow
              campaign="Awareness"
              disabled
              id={tableID}
              subtext="Ends 11/20/2021"
              text="Paused"
              type="halted"
            />
            <BaseRow
              campaign="Catalogs"
              checked
              id={tableID}
              subtext="Ends 11/20/2021"
              text="Warning"
              type="warning"
            />
            <BaseRow
              campaign="Awareness"
              checked={false}
              id={tableID}
              subtext="Ends 11/20/2021"
              text="Complete"
              type="ok"
            />
          </Table.Body>
        </Table>
      </Flex>
    </Box>
  );
}

Top captions
import { Box, Checkbox, Flex, Label, Status, Table, Text } from 'gestalt';

function HeaderRow({ id }) {
  return (
    <Table.Header>
      <Table.Row>
        <Table.HeaderCell>
          <Box display="visuallyHidden">
            <Label htmlFor={id}>Not all checkboxes are checked</Label>
          </Box>
          <Checkbox id={id} indeterminate onChange={() => {}} size="sm" />
        </Table.HeaderCell>
        {['Status', 'Campaign'].map((title) => (
          <Table.HeaderCell key={title}>
            <Text weight="bold">{title}</Text>
          </Table.HeaderCell>
        ))}
      </Table.Row>
    </Table.Header>
  );
}

function BaseRow({ id, checked, disabled, type, text, subtext, campaign }) {
  return (
    <Table.Row>
      <Table.Cell>
        <Checkbox
          checked={checked}
          disabled={disabled}
          id={`${id.replace(/ /g, '_').replace(/'/g, '')}_${text
            .replace(/ /g, '_')
            .replace(/'/g, '')}`}
          onChange={() => {}}
          size="sm"
        />
      </Table.Cell>
      <Table.Cell>
        <Status subtext={subtext} title={text} type={type} />
      </Table.Cell>
      <Table.Cell>
        <Label
          htmlFor={`${id.replace(/ /g, '_').replace(/'/g, '')}_${text
            .replace(/ /g, '_')
            .replace(/'/g, '')}`}
        >
          <Text color={disabled ? 'subtle' : 'default'}>{campaign}</Text>
        </Label>
      </Table.Cell>
    </Table.Row>
  );
}

export default function Example() {
  const tableID = 'Example of correct accessibility with bottom caption';

  return (
    <Box padding={4}>
      <Flex direction="column" gap={{ column: 2, row: 0 }}>
        <Table accessibilityLabel="Your campaigns summary">
          <HeaderRow id={tableID} />
          <Table.Body>
            <BaseRow
              campaign="Engagement"
              checked
              id={tableID}
              subtext="Ends 11/20/2021"
              text="In progress"
              type="inProgress"
            />
            <BaseRow
              campaign="Awareness"
              disabled
              id={tableID}
              subtext="Ends 11/20/2021"
              text="Paused"
              type="halted"
            />
            <BaseRow
              campaign="Catalogs"
              checked
              id={tableID}
              subtext="Ends 11/20/2021"
              text="Warning"
              type="warning"
            />
            <BaseRow
              campaign="Awareness"
              checked={false}
              id={tableID}
              subtext="Ends 11/20/2021"
              text="Complete"
              type="ok"
            />
          </Table.Body>
        </Table>
        <Box aria-hidden>
          <Text align="center" size="100">
            Your campaigns summary
          </Text>
        </Box>
      </Flex>
    </Box>
  );
}

Bottom captions

Localization

Be sure to localize all text strings. Note that localization can lengthen text by 20 to 30 percent.

Follow our guidelines on concise content and headings to account for localization.

    Wrap important table content instead of truncating. Use truncation only for secondary content, and include a tooltip to show the full text on hover.
    

Subcomponents

Table.Header

Use Table.Header to group the header content in Table.

Table.Header Props

Table.Header subcomponent props
Name
Type
Default
children
Required
ReactElement
-

Must be an instance of Table.Row. See the Subcomponent section to learn more.

display
"tableHeaderGroup" | "visuallyHidden"
"tableHeaderGroup"

Display visuallyHidden ensures the component is visually hidden but still is read by screen readers.

sticky
boolean
false

If true, the table header will be sticky and the table body will be scrollable. See the sticky Header and the sticky header and columns variants for details.

Table.Body

Use Table.Body to group the body content in Table.

Table.Body Props

Table.Body subcomponent props
Name
Type
Default
children
Required
React.ChildrenArray<ReactElement>
-

Must be instances of Table.Row and/or Table.RowExpandable components. See the Subcomponent section to learn more.

Table.Footer

Use Table.Footer to group the footer content in Table.

Table.Footer Props

Table.Footer subcomponent props
Name
Type
Default
children
Required
React.ChildrenArray<ReactElement>
-

Must be instances of Table.Row and/or Table.RowExpandable components. See the Subcomponent section to learn more.

sticky
boolean
-

If true, the table footer will be sticky and the table body will be scrollable above it. See the sticky footer and the sticky header and columns variants for details.

Table.Cell

Use Table.Cell for individual table values.

Table.Cell Props

Table.Cell subcomponent props
Name
Type
Default
children
Required
React.Node
-

The content of the table cell.

colSpan
number
-

colSpan defines the number of columns a cell should span.

rowSpan
number
-

rowSpan defines the number of rows a cell should span.

Table.HeaderCell

Use Table.HeaderCell to define a header cell in Table.

Table.HeaderCell Props

Table.HeaderCell subcomponent props
Name
Type
Default
children
Required
React.Node
-

The content of the table cell.

colSpan
number
-

colSpan defines the number of columns a cell should span.

rowSpan
number
-

rowSpan defines the number of rows a cell should span.

scope
"col" | "colgroup" | "row" | "rowgroup"
-

The scope attribute specifies whether a header cell is a header for a column, row, or group of columns or rows. The scope attribute has no visual effect, but is used by screen readers and other assistive technologies.

Table.SortableHeaderCell

Use Table.SortableHeaderCell to define a header cell with sorting functionality in Table.

Table.SortableHeaderCell Props

Table.SortableHeaderCell subcomponent props
Name
Type
Default
children
Required
React.Node
-

The content of the table cell.

onSortChange
Required
(arg1: {
  event: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>;
}) => void
-

Callback fired when the sort button component is clicked.

sortOrder
Required
"asc" | "desc"
-

Sets the sorting direction: sortOrder="asc" is ascending (A to Z) and sortOrder="desc" is descending (Z to A):

status
Required
"active" | "inactive"
-

Disables the sorting functionality for a column.

align
"start" | "end"
"start"

Sets the alignment of the cell content and reverses the sort icon position.

colSpan
number
-

colSpan defines the number of columns a cell should span.

rowSpan
number
-

rowSpan defines the number of rows a cell should span.

scope
"col" | "colgroup" | "row" | "rowgroup"
-

The scope attribute specifies whether a header cell is a header for a column, row, or group of columns or rows. The scope attribute has no visual effect in ordinary web browsers, but can be used by screen readers.

Table.Row

Use Table.Row to define a row in Table.

Table.Row Props

Table.Row subcomponent props
Name
Type
Default
children
Required
React.ChildrenArray<ReactElement>
-

Must be instances of Table.Cell, Table.HeaderCell, or Table.SortableHeaderCell components. See the Subcomponent section to learn more.

hoverStyle
"gray" | "none"
"none"

Sets the background color on hover over the row. See the selected and hovered state variant to learn more.

selected
"selected" | "unselected"
-

Indicates if Table.Row is currently selected or unselected. See the selected and hovered state variant to learn more.

Table.RowExpandable

Use Table.RowExpandable to define a row that expands and collapses additional content.

Table.RowExpandable Props

Table.RowExpandable subcomponent props
Name
Type
Default
accessibilityCollapseLabel
Required
string
-

Supply a short, descriptive label for screen-readers as a text alternative to the collapse button. Accessibility: It populates aria-label on the <button> element for the collapse button.

accessibilityExpandLabel
Required
string
-

Supply a short, descriptive label for screen-readers as a text alternative to the expand button.

children
Required
React.ChildrenArray<ReactElement>
-

Must be instances of Table.Cell. See the Subcomponent section to learn more.

expandedContents
Required
React.Node
-

The contents to show and/or hide on an expandable row. Required when using Table.RowExpandable as a controlled component. See the controlled/uncontrolled Table.RowExpandable section to learn more.

id
Required
string
-

Unique id for Table.RowExpandable.

expanded
boolean
-

When passed Row.TableRowExpandable becomes a controlled component. If not passed, it stays uncontrolled. See the controlled/uncontrolled Table.RowExpandable section to learn more.

hoverStyle
"gray" | "none"
"gray"

Sets the background color on hover over the row. See the selected and hovered state variant to learn more.

onExpand
(arg1: {
  event: React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLButtonElement>;
  expanded: boolean;
}) => void
-

Callback fired when the expand button component is clicked.

selected
"selected" | "unselected"
-

Indicates if Table.RowExpandable is currently selected or unselected. See the selected and hovered state variant to learn more.

Table.RowDrawer

Use Table.RowDrawer to define a row drawer to display additional content.

Table.RowDrawer Props

Table.RowDrawer subcomponent props
Name
Type
Default
children
Required
React.ChildrenArray<ReactElement>
-

Must be instances of Table.Cell. See the Subcomponent section to learn more.

drawerContents
Required
React.Node
-

The contents within the drawer. See the Table.RowDrawer implementation to learn more.

id
Required
string
-

Unique id for Table.RowDrawer.

hoverStyle
"gray" | "none"
"none"

Sets the background color on hover over the row. See the selected and hovered state variant to learn more.

selected
"selected" | "unselected"
-

Indicates if Table.RowDrawer is currently selected or unselected. See the selected and hovered state variant to learn more.

Variants

import { Box, Table, Text } from 'gestalt';

export default function Example() {
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Table accessibilityLabel="Sticky footer" maxHeight={200}>
        <Table.Header sticky>
          <Table.Row>
            <Table.HeaderCell>
              <Text weight="bold">Campaign</Text>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text weight="bold">Impression</Text>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text weight="bold">Cost</Text>
            </Table.HeaderCell>
          </Table.Row>
        </Table.Header>
        <Table.Body>
          <Table.Row>
            <Table.Cell>
              <Text>Spring season</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>10,000</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>$500</Text>
            </Table.Cell>
          </Table.Row>
          <Table.Row>
            <Table.Cell>
              <Text>Autumn season</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>10,000</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>$500</Text>
            </Table.Cell>
          </Table.Row>
          <Table.Row>
            <Table.Cell>
              <Text>Summer season</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>10,000</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>$500</Text>
            </Table.Cell>
          </Table.Row>
          <Table.Row>
            <Table.Cell>
              <Text>Winter season</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>10,000</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>$500</Text>
            </Table.Cell>
          </Table.Row>
        </Table.Body>
        <Table.Footer sticky>
          <Table.Row>
            <Table.Cell>
              <Text weight="bold">Total</Text>
            </Table.Cell>
            <Table.Cell>
              <Text weight="bold">40,000</Text>
            </Table.Cell>
            <Table.Cell>
              <Text weight="bold">$2,000</Text>
            </Table.Cell>
          </Table.Row>
        </Table.Footer>
      </Table>
    </Box>
  );
}

Sticky Column

Try scrolling horizontally to see the first column remain in place.

import { Box, Image, Mask, Table, Text } from 'gestalt';

export default function Example() {
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box width="60%">
        <Table
          accessibilityLabel="Sticky Column"
          maxHeight={200}
          stickyColumns={1}
        >
          <Table.Header>
            <Table.Row>
              <Table.HeaderCell>
                <Text weight="bold">Image</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Name</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Super Name</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Favorite Food</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Best Friend</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Birthday</Text>
              </Table.HeaderCell>
            </Table.Row>
          </Table.Header>

          <Table.Body>
            <Table.Row>
              <Table.Cell>
                <Box width={50}>
                  <Mask rounding="circle">
                    <Image
                      alt="Tony"
                      naturalHeight={50}
                      naturalWidth={50}
                      src="https://i.ibb.co/r3R04Y9/ironman.jpg"
                    />
                  </Mask>
                </Box>
              </Table.Cell>
              <Table.Cell>
                <Text>Tony Stark</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Iron Man</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Shawarma</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Spiderman</Text>
              </Table.Cell>
              <Table.Cell>
                <Box width={200}>
                  <Text>May 29, 1970</Text>
                </Box>
              </Table.Cell>
            </Table.Row>

            <Table.Row>
              <Table.Cell>
                <Box width={50}>
                  <Mask rounding="circle">
                    <Image
                      alt="Peter"
                      naturalHeight={50}
                      naturalWidth={50}
                      src="https://i.ibb.co/64NxM43/spiderman.png"
                    />
                  </Mask>
                </Box>
              </Table.Cell>
              <Table.Cell>
                <Text>Peter Parker</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Spiderman</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Sandwiches</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Iron Man</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>December 28, 1995</Text>
              </Table.Cell>
            </Table.Row>

            <Table.Row>
              <Table.Cell>
                <Box width={50}>
                  <Mask rounding="circle">
                    <Image
                      alt="T'Challa"
                      naturalHeight={50}
                      naturalWidth={50}
                      src="https://i.ibb.co/GpNtW5N/black-Panther.png"
                    />
                  </Mask>
                </Box>
              </Table.Cell>
              <Table.Cell>
                <Text>T&apos;Challa</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Black Panther</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Beef suya</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Shuri</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>November 28, 1977</Text>
              </Table.Cell>
            </Table.Row>
          </Table.Body>
        </Table>
      </Box>
    </Box>
  );
}

Multiple sticky columns

Try scrolling horizontally to see the first 3 columns remain in place.

import { Box, Image, Mask, Table, Text } from 'gestalt';

export default function Example() {
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box width="60%">
        <Table
          accessibilityLabel="Multiple sticky columns"
          borderStyle="none"
          maxHeight={200}
          stickyColumns={3}
        >
          <Table.Header>
            <Table.Row>
              <Table.HeaderCell>
                <Text weight="bold">Image</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Name</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Super Name</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Best Friend</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Favorite Food</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Super Powers</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Home</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Aliases</Text>
              </Table.HeaderCell>
            </Table.Row>
          </Table.Header>

          <Table.Body>
            <Table.Row>
              <Table.Cell>
                <Box width={50}>
                  <Mask rounding="circle">
                    <Image
                      alt="Tony"
                      naturalHeight={50}
                      naturalWidth={50}
                      src="https://i.ibb.co/r3R04Y9/ironman.jpg"
                    />
                  </Mask>
                </Box>
              </Table.Cell>
              <Table.Cell>
                <Text>Tony Stark</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Iron Man</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Spiderman</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Shawarma</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Flight, Super strength</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>New York</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>N/A</Text>
              </Table.Cell>
            </Table.Row>

            <Table.Row>
              <Table.Cell>
                <Box width={50}>
                  <Mask rounding="circle">
                    <Image
                      alt="Peter"
                      naturalHeight={50}
                      naturalWidth={50}
                      src="https://i.ibb.co/64NxM43/spiderman.png"
                    />
                  </Mask>
                </Box>
              </Table.Cell>
              <Table.Cell>
                <Text>Peter Parker</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Spiderman</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Iron Man</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Sandwiches</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Spidey senses, super strength, web shooters</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Brooklyn</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Friendly Neighborhood Spiderman</Text>
              </Table.Cell>
            </Table.Row>

            <Table.Row>
              <Table.Cell>
                <Box width={50}>
                  <Mask rounding="circle">
                    <Image
                      alt="Wanda"
                      naturalHeight={50}
                      naturalWidth={50}
                      src="https://i.ibb.co/hV6Vpbf/scarlet.png"
                    />
                  </Mask>
                </Box>
              </Table.Cell>
              <Table.Cell>
                <Text>Wanda Maximoff</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Scarlet Witch</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Vision</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Chicken paprikash</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Chaos magic, spells, reality warping</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Sokovia</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>N/A</Text>
              </Table.Cell>
            </Table.Row>

            <Table.Row>
              <Table.Cell>
                <Box width={50}>
                  <Mask rounding="circle">
                    <Image
                      alt="Black Panther"
                      naturalHeight={50}
                      naturalWidth={50}
                      src="https://i.ibb.co/GpNtW5N/black-Panther.png"
                    />
                  </Mask>
                </Box>
              </Table.Cell>
              <Table.Cell>
                <Text>T&apos;Challa</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Black Panther</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Shuri</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Beef suya</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Enhanced strength, speed, reflexes + Vibranium suit</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Wakanda</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>King of the Dead</Text>
              </Table.Cell>
            </Table.Row>
          </Table.Body>
        </Table>
      </Box>
    </Box>
  );
}

Sticky header and sticky columns

Try scrolling horizontally and vertically to see the columns and header remain in place.

import { Box, Image, Mask, Table, Text } from 'gestalt';

export default function Example() {
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box width="60%">
        <Table
          accessibilityLabel="Sticky header and sticky columns"
          borderStyle="none"
          maxHeight={200}
          stickyColumns={3}
        >
          <Table.Header sticky>
            <Table.Row>
              <Table.HeaderCell>
                <Text weight="bold">Image</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Name</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Super Name</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Best Friend</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Favorite Food</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Super Powers</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Home</Text>
              </Table.HeaderCell>
              <Table.HeaderCell>
                <Text weight="bold">Aliases</Text>
              </Table.HeaderCell>
            </Table.Row>
          </Table.Header>

          <Table.Body>
            <Table.Row>
              <Table.Cell>
                <Box width={50}>
                  <Mask rounding="circle">
                    <Image
                      alt="Tony"
                      naturalHeight={50}
                      naturalWidth={50}
                      src="https://i.ibb.co/r3R04Y9/ironman.jpg"
                    />
                  </Mask>
                </Box>
              </Table.Cell>
              <Table.Cell>
                <Text>Tony Stark</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Iron Man</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Spiderman</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Shawarma</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Flight, Super strength</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>New York</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>N/A</Text>
              </Table.Cell>
            </Table.Row>

            <Table.Row>
              <Table.Cell>
                <Box width={50}>
                  <Mask rounding="circle">
                    <Image
                      alt="Peter"
                      naturalHeight={50}
                      naturalWidth={50}
                      src="https://i.ibb.co/64NxM43/spiderman.png"
                    />
                  </Mask>
                </Box>
              </Table.Cell>
              <Table.Cell>
                <Text>Peter Parker</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Spiderman</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Iron Man</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Sandwiches</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Spidey senses, super strength, web shooters</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Brooklyn</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Friendly Neighborhood Spiderman</Text>
              </Table.Cell>
            </Table.Row>

            <Table.Row>
              <Table.Cell>
                <Box width={50}>
                  <Mask rounding="circle">
                    <Image
                      alt="Wanda"
                      naturalHeight={50}
                      naturalWidth={50}
                      src="https://i.ibb.co/hV6Vpbf/scarlet.png"
                    />
                  </Mask>
                </Box>
              </Table.Cell>
              <Table.Cell>
                <Text>Wanda Maximoff</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Scarlet Witch</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Vision</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Chicken paprikash</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Chaos magic, spells, reality warping</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Sokovia</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>N/A</Text>
              </Table.Cell>
            </Table.Row>

            <Table.Row>
              <Table.Cell>
                <Box width={50}>
                  <Mask rounding="circle">
                    <Image
                      alt="Black Panther"
                      naturalHeight={50}
                      naturalWidth={50}
                      src="https://i.ibb.co/GpNtW5N/black-Panther.png"
                    />
                  </Mask>
                </Box>
              </Table.Cell>
              <Table.Cell>
                <Text>T&apos;Challa</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Black Panther</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Shuri</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Beef suya</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Enhanced strength, speed, reflexes + Vibranium suit</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>Wakanda</Text>
              </Table.Cell>
              <Table.Cell>
                <Text>King of the Dead</Text>
              </Table.Cell>
            </Table.Row>
          </Table.Body>
        </Table>
      </Box>
    </Box>
  );
}

Controlled/Uncontrolled Table.RowExpandable

To set Table.RowExpandable to be a controlled component, use the expanded prop. When expanded is not passed (expanded set to undefined), Table.RowExpandable stays uncontrolled. Use onExpand prop to have access to the internal state of the component via render props ({ event, expanded }) => { expanded }"

When Table.RowExpandable is uncontrolled, use the clickable expand/collapse icon button to hide/show the content.

Table.RowExpandable with Sticky Columns

When specifying stickyColumns with expandable rows, include the column of arrows in your count. This example sets stickyColumns to 3.

import { useState } from 'react';
import { Avatar, Box, Link, Table, Text, WashAnimated } from 'gestalt';

export default function Example() {
  const [textShown, setTextShown] = useState(false);
  const showTextOnExpand = () => <Text>Row expanded</Text>;

  return (
    <Box margin="auto" padding={4} width="70%">
      <Table
        accessibilityLabel="Table.RowExpandable with Sticky Columns"
        stickyColumns={3}
      >
        <Table.Header>
          <Table.Row>
            <Table.HeaderCell>
              <Box display="visuallyHidden">
                <Text weight="bold">Open/Close row</Text>
              </Box>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text weight="bold">Name</Text>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text weight="bold">Super Name</Text>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text weight="bold">Best Friend</Text>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text weight="bold">Favorite Food</Text>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text weight="bold">Home</Text>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text weight="bold">Alias</Text>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Box width={200}>
                <Text weight="bold">Super Powers</Text>
              </Box>
            </Table.HeaderCell>
          </Table.Row>
        </Table.Header>

        <Table.Body>
          <Table.RowExpandable
            accessibilityCollapseLabel="Collapse"
            accessibilityExpandLabel="Expand"
            expandedContents={
              <Box column={12} maxWidth={236} padding={2}>
                <WashAnimated
                  image={
                    <Avatar
                      name="tony avatar"
                      src="https://i.ibb.co/8948ym5/avenge.png"
                    />
                  }
                >
                  <Text align="center" weight="bold">
                    <Link href="https://pinterest.com">
                      <Box paddingX={3} paddingY={2}>
                        Tony&apos;s Info
                      </Box>
                    </Link>
                  </Text>
                  {textShown && showTextOnExpand()}
                </WashAnimated>
              </Box>
            }
            id="row1"
            onExpand={() => setTextShown(!textShown)}
          >
            <Table.Cell>
              <Text>Tony Stark</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Iron Man</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Spiderman</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Shawarma</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>New York City</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>N/A</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Flight, Super strength</Text>
            </Table.Cell>
          </Table.RowExpandable>

          <Table.RowExpandable
            accessibilityCollapseLabel="Collapse"
            accessibilityExpandLabel="Expand"
            expandedContents={
              <Table accessibilityLabel="none">
                <Table.Header sticky>
                  <Table.Row>
                    <Table.HeaderCell>
                      <Text weight="bold">Name</Text>
                    </Table.HeaderCell>
                    <Table.HeaderCell>
                      <Text weight="bold">Relationship</Text>
                    </Table.HeaderCell>
                  </Table.Row>
                </Table.Header>
                <Table.Body>
                  <Table.Row>
                    <Table.Cell>
                      <Text>Vision</Text>
                    </Table.Cell>
                    <Table.Cell>
                      <Text>Husband</Text>
                    </Table.Cell>
                  </Table.Row>
                  <Table.Row>
                    <Table.Cell>
                      <Text>Wiccan</Text>
                    </Table.Cell>
                    <Table.Cell>
                      <Text>Child</Text>
                    </Table.Cell>
                  </Table.Row>
                  <Table.Row>
                    <Table.Cell>
                      <Text>Speed</Text>
                    </Table.Cell>
                    <Table.Cell>
                      <Text>Child</Text>
                    </Table.Cell>
                  </Table.Row>
                </Table.Body>
              </Table>
            }
            id="row2"
          >
            <Table.Cell>
              <Text>Wanda Maximoff</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Scarlet Witch</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Vision</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Chicken paprikash</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Sokovia</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Wanda Frank</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Chaos magic, spells, reality warping</Text>
            </Table.Cell>
          </Table.RowExpandable>

          <Table.RowExpandable
            accessibilityCollapseLabel="Collapse"
            accessibilityExpandLabel="Expand"
            expandedContents={
              <Box column={12} maxWidth={236} padding={2}>
                <WashAnimated
                  image={
                    <Avatar
                      name="Black panther avatar"
                      src="https://i.ibb.co/GpNtW5N/black-Panther.png"
                    />
                  }
                >
                  <Text align="center" weight="bold">
                    <Link href="https://pinterest.com">
                      <Box paddingX={3} paddingY={2}>
                        Black Panther&apos;s Info
                      </Box>
                    </Link>
                  </Text>
                </WashAnimated>
              </Box>
            }
            id="row3"
          >
            <Table.Cell>
              <Text>T&apos;Challa</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Black Panther</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Shuri</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Beef suya</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Wakana</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>King of the Dead</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Enhanced strength, speed, reflexes + Vibranium suit</Text>
            </Table.Cell>
          </Table.RowExpandable>
        </Table.Body>
      </Table>
    </Box>
  );
}

Table.RowDrawer implementation

Drawer row that is able to hold additional content.

import { useState } from 'react';
import { BannerSlim, Box, Table, Text } from 'gestalt';

export default function Example() {
  const [showdrawer, setShowDrawer] = useState(true);

  return (
    <Box width="100%">
      <Table accessibilityLabel="Table.RowDrawer example">
        <colgroup>
          <col span="1" style={{ width: '60%' }} />
          <col span="1" style={{ width: '15%' }} />
          <col span="1" style={{ width: '15%' }} />
          <col span="1" style={{ width: '15%' }} />
        </colgroup>
        <Table.Header>
          <Table.Row>
            <Table.HeaderCell>
              <Text weight="bold">Campaign</Text>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text align="forceRight" weight="bold">
                Spend
              </Text>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text align="forceRight" weight="bold">
                Impressions
              </Text>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text align="forceRight" weight="bold">
                CTR
              </Text>
            </Table.HeaderCell>
          </Table.Row>
        </Table.Header>

        <Table.Body>
          <Table.RowDrawer
            drawerContents={
              showdrawer ? (
                <BannerSlim
                  iconAccessibilityLabel="Recommendation"
                  message="Increasing your daily spend could increase clicks by 20%"
                  onDismiss={() => setShowDrawer(false)}
                  primaryAction={{
                    accessibilityLabel: 'Apply for increasing your daily spend',
                    label: 'Apply',
                    onClick: () => {},
                    role: 'button',
                  }}
                  type="recommendation"
                />
              ) : null
            }
            id="drawerExample"
          >
            <Table.Cell>
              <Text>Training treats</Text>
            </Table.Cell>
            <Table.Cell>
              <Text align="forceRight">$3,200</Text>
            </Table.Cell>
            <Table.Cell>
              <Text align="forceRight">3.4k</Text>
            </Table.Cell>
            <Table.Cell>
              <Text align="forceRight">0.07%</Text>
            </Table.Cell>
          </Table.RowDrawer>
          <Table.Row>
            <Table.Cell>
              <Text>Vegan cuisine</Text>
            </Table.Cell>
            <Table.Cell>
              <Text align="forceRight">$4,200</Text>
            </Table.Cell>
            <Table.Cell>
              <Text align="forceRight">5k</Text>
            </Table.Cell>
            <Table.Cell>
              <Text align="forceRight">0.40%</Text>
            </Table.Cell>
          </Table.Row>
          <Table.Row>
            <Table.Cell>
              <Text>Mexican cuisine</Text>
            </Table.Cell>
            <Table.Cell>
              <Text align="forceRight">$5,000</Text>
            </Table.Cell>
            <Table.Cell>
              <Text align="forceRight">20k</Text>
            </Table.Cell>
            <Table.Cell>
              <Text align="forceRight">0.10%</Text>
            </Table.Cell>
          </Table.Row>
        </Table.Body>
      </Table>
    </Box>
  );
}

Sortable header cells

Sortable header cells are clickable in an accessible way and have an icon to display whether the table is currently being sorted by that column.

Depending on the table contents, use the align property to set the alignment of the header cell and sort icon position.

import { useState } from 'react';
import { Box, Table, Text } from 'gestalt';

export default function SortableHeaderExample() {
  const [sortOrder, setSortOrder] = useState('desc');
  const [sortCol, setSortCol] = useState('name');

  const onSortChange = (col) => {
    if (sortCol !== col) {
      setSortCol(col);
      setSortOrder('desc');
    } else {
      setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
    }
  };

  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
    >
      <Table accessibilityLabel="Sortable header cells">
        <colgroup>
          <col span="1" style={{ width: '10%' }} />
          <col span="1" style={{ width: '10%' }} />
          <col span="1" style={{ width: '80%' }} />
        </colgroup>
        <Table.Header>
          <Table.Row>
            <Table.SortableHeaderCell
              onSortChange={() => onSortChange('id')}
              sortOrder={sortOrder}
              status={sortCol === 'id' ? 'active' : 'inactive'}
            >
              <Text weight="bold">Id</Text>
            </Table.SortableHeaderCell>
            <Table.SortableHeaderCell
              onSortChange={() => onSortChange('name')}
              sortOrder={sortOrder}
              status={sortCol === 'name' ? 'active' : 'inactive'}
            >
              <Text weight="bold">Name</Text>
            </Table.SortableHeaderCell>

            <Table.SortableHeaderCell
              align="end"
              onSortChange={() => onSortChange('cost')}
              sortOrder={sortOrder}
              status={sortCol === 'cost' ? 'active' : 'inactive'}
            >
              <Text weight="bold">Cost</Text>
            </Table.SortableHeaderCell>
          </Table.Row>
        </Table.Header>
        <Table.Row>
          <Table.Cell>
            <Text>123</Text>
          </Table.Cell>
          <Table.Cell>
            <Text>Snax</Text>
          </Table.Cell>
          <Table.Cell>
            <Text align="end">$50</Text>
          </Table.Cell>
        </Table.Row>
      </Table>
    </Box>
  );
}

Sortable header cells with sticky columns

import { useState } from 'react';
import { Box, Table, Text } from 'gestalt';

export default function SortableHeaderExample() {
  const [sortOrder, setSortOrder] = useState('desc');
  const [sortCol, setSortCol] = useState('name');

  const onSortChange = (col) => {
    if (sortCol !== col) {
      setSortCol(col);
      setSortOrder('desc');
    } else {
      setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
    }
  };

  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
    >
      <Box width="70%">
        <Table
          accessibilityLabel="Sortable header cells with sticky columns"
          stickyColumns={2}
        >
          <Table.Header>
            <Table.Row>
              <Table.SortableHeaderCell
                onSortChange={() => onSortChange('name')}
                sortOrder={sortOrder}
                status={sortCol === 'name' ? 'active' : 'inactive'}
              >
                <Text weight="bold">Name</Text>
              </Table.SortableHeaderCell>
              <Table.SortableHeaderCell
                onSortChange={() => onSortChange('id')}
                sortOrder={sortOrder}
                status={sortCol === 'id' ? 'active' : 'inactive'}
              >
                <Text weight="bold">Nickname</Text>
              </Table.SortableHeaderCell>
              <Table.SortableHeaderCell
                onSortChange={() => onSortChange('food')}
                sortOrder={sortOrder}
                status={sortCol === 'food' ? 'active' : 'inactive'}
              >
                <Text weight="bold">Favorite Food</Text>
              </Table.SortableHeaderCell>
              <Table.SortableHeaderCell
                onSortChange={() => onSortChange('friend')}
                sortOrder={sortOrder}
                status={sortCol === 'friend' ? 'active' : 'inactive'}
              >
                <Text weight="bold">Best Friend</Text>
              </Table.SortableHeaderCell>
              <Table.SortableHeaderCell
                onSortChange={() => onSortChange('birth')}
                sortOrder={sortOrder}
                status={sortCol === 'birth' ? 'active' : 'inactive'}
              >
                <Text weight="bold">Birthdate</Text>
              </Table.SortableHeaderCell>
              <Table.SortableHeaderCell
                onSortChange={() => onSortChange('desc')}
                sortOrder={sortOrder}
                status={sortCol === 'desc' ? 'active' : 'inactive'}
              >
                <Text weight="bold">Description</Text>
              </Table.SortableHeaderCell>
              <Table.SortableHeaderCell
                onSortChange={() => onSortChange('color')}
                sortOrder={sortOrder}
                status={sortCol === 'color' ? 'active' : 'inactive'}
              >
                <Text weight="bold">Favorite Color</Text>
              </Table.SortableHeaderCell>
            </Table.Row>
          </Table.Header>
        </Table>
      </Box>
    </Box>
  );
}

Selected & hovered state

Table.Row, Table.RowExpandable and Table.RowDrawer support hovered and selected states.

If a row subcomponent is selectable, toggle the selected prop between "selected" and "unselected" to keep a constant border space in the row that is only visible when the row is selected.

If the row is not selectable, the selected prop should not be set. In this case, it doesn't set a side border.

import { useState } from 'react';
import { Box, Checkbox, Table, Text } from 'gestalt';

export default function Example() {
  const [selected, setSelectedRow] = useState([]);

  return (
    <Box height="100%" overflow="scroll" width="100%">
      <Table accessibilityLabel="Selected row example table">
        <Table.Header>
          <Table.Row>
            <Table.HeaderCell>
              <Text />
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text weight="bold">Selected</Text>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text weight="bold">Column</Text>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text weight="bold">Column</Text>
            </Table.HeaderCell>
            <Table.HeaderCell>
              <Text weight="bold">Subcomponent</Text>
            </Table.HeaderCell>
          </Table.Row>
        </Table.Header>
        <Table.Body>
          <Table.Row
            key="row1"
            hoverStyle="gray"
            selected={selected.includes('row') ? 'selected' : 'unselected'}
          >
            <Table.Cell>
              <Text />
            </Table.Cell>
            <Table.Cell>
              <Checkbox
                checked={selected.includes('row')}
                id="Row"
                onChange={() =>
                  setSelectedRow((value) =>
                    value.includes('row')
                      ? value.filter((item) => item !== 'row')
                      : [...value, 'row']
                  )
                }
                size="sm"
              />
            </Table.Cell>
            <Table.Cell>
              <Text>value</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>value</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Table.Row</Text>
            </Table.Cell>
          </Table.Row>
          <Table.RowExpandable
            key="row2"
            accessibilityCollapseLabel="Collapse"
            accessibilityExpandLabel="Expand"
            expandedContents={<Text>Content</Text>}
            hoverStyle="gray"
            id="row2"
            selected={
              selected.includes('rowExpandable') ? 'selected' : 'unselected'
            }
          >
            <Table.Cell>
              <Checkbox
                checked={selected.includes('rowExpandable')}
                id="RowExpandable"
                onChange={() =>
                  setSelectedRow((value) =>
                    value.includes('rowExpandable')
                      ? value.filter((item) => item !== 'rowExpandable')
                      : [...value, 'rowExpandable']
                  )
                }
                size="sm"
              />
            </Table.Cell>
            <Table.Cell>
              <Text>value</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>value</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Table.RowExpandable</Text>
            </Table.Cell>
          </Table.RowExpandable>
          <Table.RowDrawer
            key="row3"
            drawerContents={<Text>Content</Text>}
            hoverStyle="gray"
            id="row3"
            selected={
              selected.includes('rowDrawer') ? 'selected' : 'unselected'
            }
          >
            <Table.Cell>
              <Text />
            </Table.Cell>
            <Table.Cell>
              <Checkbox
                checked={selected.includes('rowDrawer')}
                id="rowDrawer"
                onChange={() =>
                  setSelectedRow((value) =>
                    value.includes('rowDrawer')
                      ? value.filter((item) => item !== 'rowDrawer')
                      : [...value, 'rowDrawer']
                  )
                }
                size="sm"
              />
            </Table.Cell>
            <Table.Cell>
              <Text>value</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>value</Text>
            </Table.Cell>
            <Table.Cell>
              <Text>Table.RowDrawer</Text>
            </Table.Cell>
          </Table.RowDrawer>
        </Table.Body>
      </Table>
    </Box>
  );
}

Component quality checklist

Component quality checklist
Quality item
Status
Status description
Figma Library
Partially ready
Component is live in Figma, however may not be available for all platforms.
Responsive Web
Ready
Component responds to changing viewport sizes in web and mobile web.

Accordion
Modules are another way to stack multiple rows of content. However, they are used to show 2 to 3 blocks of related content, whereas Tables are used for large data sets that can be easily scanned and compared across multiple rows and columns.

Checkbox
Checkboxes are often used in tables to allow for selecting and editing of multiple rows at once.