Skip to content
Components

Table

A component for displaying large amounts of data in rows and columns.

Basic usage

Table is a set of building blocks that render a native <table>. Import the Table namespace and compose its parts: Table.Table, Table.Head, Table.HeaderRow, Table.HeaderCell, Table.Body, Table.Row and Table.Cell.

caption is required and describes the table content. It isn't displayed. Every Table.HeaderCell needs a scope so screen readers can tie it to its data cells.

import {Table} from '@pleo-io/telescope'


const Heroes = () => (
    <Table.Table caption="Super heroes and their real names">
        <Table.Head>
            <Table.HeaderRow>
                <Table.HeaderCell scope="col">Super hero</Table.HeaderCell>
                <Table.HeaderCell scope="col">Real name</Table.HeaderCell>
            </Table.HeaderRow>
        </Table.Head>
        <Table.Body>
            <Table.Row>
                <Table.Cell>Iron Man</Table.Cell>
                <Table.Cell>Tony Stark</Table.Cell>
            </Table.Row>
            <Table.Row>
                <Table.Cell>Batman</Table.Cell>
                <Table.Cell>Bruce Wayne</Table.Cell>
            </Table.Row>
        </Table.Body>
    </Table.Table>
)

Alignment

Right-align numeric columns with $textAlign on both the header and the data cells.

<Table.HeaderCell scope="col" $textAlign="right">
    Amount
</Table.HeaderCell>
...
<Table.Cell $textAlign="right">1,000.00</Table.Cell>

Clickable and active rows

$isClickable adds a hover state and pointer cursor. Make the row keyboard reachable with tabIndex={0} and handle both click and Enter. $isActive outlines the row, for example to mark the row whose details are open in a drawer.

<Table.Row
    $isClickable
    $isActive={selectedId === expense.id}
    tabIndex={0}
    onClick={() => open(expense.id)}
    onKeyDown={(event) => event.key === 'Enter' && open(expense.id)}
>
    <Table.Cell>{expense.merchant}</Table.Cell>
</Table.Row>

Cell content with subtext

Use Table.CellContent to show a quieter second line, e.g. an email under a name. Both lines truncate rather than wrap, so rows keep the same height with or without subtext.

<Table.Cell>
    <Table.CellContent subtext="tony@stark.com">Tony Stark</Table.CellContent>
</Table.Cell>

Table.CellLink and Table.CellAction are styled for use inside a cell.

<Table.Cell>
    <Table.CellLink href={`/people/${person.id}`}>{person.name}</Table.CellLink>
</Table.Cell>
<Table.Cell>
    <Table.CellAction onClick={() => edit(person.id)}>Edit</Table.CellAction>
</Table.Cell>

Group summary rows

Table.GroupSummaryRow renders a summary row, e.g. totals for a group. $isHighlighted gives it a louder background.

<Table.GroupSummaryRow $isHighlighted>
    <Table.Cell>3 expenses</Table.Cell>
    <Table.Cell $textAlign="right">1,200.00</Table.Cell>
</Table.GroupSummaryRow>

Scrolling and sticky header

Wrap the table in Table.ScrollableWrapper to scroll it within a constrained area. Pass $stickyHeader to keep the header visible while the body scrolls.

<Table.ScrollableWrapper $stickyHeader style={{height: 400}}>
    <Table.Table caption="Expenses">...</Table.Table>
</Table.ScrollableWrapper>

Virtualization

When only a window of rows is rendered, pass the data index of the first rendered row as virtualStartIndex so the zebra striping doesn't flicker while scrolling.

<Table.Table caption="Expenses" virtualStartIndex={virtualRows[0]?.index ?? 0}>
    ...
</Table.Table>

API reference

Table.Table

PropTypeDefault
caption (required)
string
virtualStartIndex
enum
0

Table.HeaderCell

PropTypeDefault
$textAlign
enum
'left'
$withBorder
boolean
scope (required)
enum

Table.Row

PropTypeDefault
$isActive
boolean
$isClickable
boolean

Table.Cell

PropTypeDefault
$textAlign
enum
'left'
$withBorder
boolean

Table.CellContent

PropTypeDefault
subtext
enum

Table.GroupSummaryRow

PropTypeDefault
$isHighlighted
boolean

Table.ScrollableWrapper

PropTypeDefault
$stickyHeader
boolean
Didn't find what you were looking for?
Edit this page