Docs
Table

Table

Table is a composite component: you assemble the structure manually using subcomponents (TableHeader, TableBody, TableRow, TableCell, etc.). Unlike DataTable, there is no virtualization here — the component renders a semantic HTML <table>, ideal for small/medium tables where accessibility and structure matter.

Includes optional search, a pagination bar (display-only or navigable — you own the page state), column totals, selection checkboxes, density control and sticky header support.

When to use

✅ Use when…🚫 Avoid when…
  • Small/medium tables (up to a few dozen rows) where semantic HTML (<table>/<tr>/<td>) brings real benefits — screen readers, SEO, copy-and-paste of cells.
  • When you want to manually compose each cell's content (links, tooltips, buttons, badges) without mapping via column configuration.
  • Cases with simple pagination (server- or client-side slices you control), local search and column totals.
  • Large data volumes (hundreds/thousands of rows). Use DataTable — it comes with virtualization and native per-column resize/sorting.

Table elements

The composite API uses the following components:

  • Table — root, controls state (search, selection, density).
  • TableHeader (Thead) — header (<thead>).
  • TableBody (Tbody) — body (<tbody>).
  • TableRow (Tr) — row (<tr>).
  • TableCell (Td) — data cell (<td>).
  • TableHeaderCell (Th) — header cell (<th>).
  • TableCheckboxCell (Tdc) — cell with a selection checkbox.
  • TableHeaderCheckboxCell (Thc) — header with a "select all" checkbox.
  • TableFooter (Tfooter) — footer (<tfoot>).
  • TableEmpty — empty state (rendered automatically when the search returns no results).
  • TablePagination — pagination bar (range, rows per page, optional previous/next navigation).

The short aliases (Thead, Tbody, Tr, etc.) and the long notation (TableHeader, TableBody, etc.) point to the same component — use whichever matches your project's style.

Basic usage

Pokedex No.
Name
Type
001
Bulbasaur
Grass
004
Charmander
Fire
007
Squirtle
Water
025
Pikachu
Electric
import {
  Table,
  TableHeader,
  TableBody,
  TableRow,
  TableCell,
  TableHeaderCell,
} from '@apollion-dsi/core/data-display/table';
 
<Table title="Mini Pokedex">
  <TableHeader>
    <TableRow>
      <TableHeaderCell>Pokedex No.</TableHeaderCell>
      <TableHeaderCell>Name</TableHeaderCell>
      <TableHeaderCell>Type</TableHeaderCell>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>001</TableCell>
      <TableCell>Bulbasaur</TableCell>
      <TableCell>Grass</TableCell>
    </TableRow>
  </TableBody>
</Table>;

Search

Enable with searching. The input appears above the table; when typing and pressing Enter, rows that do not match the term are hidden via display: none.

No.
Name
Type
001
Bulbasaur
Grass
004
Charmander
Fire
007
Squirtle
Water
025
Pikachu
Electric
<Table title="Mini Pokedex" searching>
  {/* ... */}
</Table>

Striped and densities

striped applies zebra striping; rowSize controls density (Compact, Default, Expansive). With rowTypes, the user switches density at runtime via a dropdown.

No.
Name
Type
001
Bulbasaur
Grass
004
Charmander
Fire
007
Squirtle
Water
025
Pikachu
Electric
<Table striped rowSize="Compact">{/* ... */}</Table>
<Table rowTypes>{/* dropdown enabled */}</Table>

Sticky header

stickyHeader pins the <thead> to the top while scrolling — combine with a wrapper that has max-height so the scroll actually happens:

<Paper maxHeight="500px">
  <Table stickyHeader>{/* ... */}</Table>
</Paper>

Alignment

Global (string) or per column (array). Pick one mode per Table instance — mixing a global string with a per-column array on the same table is not supported:

<Table align="right">{/* ... */}</Table>
 
<Table
  align={[
    { direction: 'left', target: '3' },
    { direction: 'right', target: '5' },
  ]}
>{/* ... */}</Table>

Per-column width

columnSize takes an array — each entry becomes width (without type), min-width or max-width depending on type. Empty strings keep the natural width:

<Table columnSize={[{ width: '100' }, '', { width: 250, type: 'min' }]}>{/* ... */}</Table>

Ellipsis

Truncates long content with and shows the full text in a tooltip on hover. Useful for narrow tables with variable content.

ID
Description
Status
001
Implement the CSV export feature with advanced filters.
OK
002
Refactor the authentication module to support SSO via SAML 2.0.
In progress
003
Update backend dependencies to LTS versions.
OK
<Table ellipsis>{/* ... */}</Table>

Hoverable

Highlights the row under the cursor — useful in long tables for visual feedback on which row is being read.

No.
Name
Type
001
Bulbasaur
Grass
004
Charmander
Fire
007
Squirtle
Water
025
Pikachu
Electric
<Table hoverable>{/* ... */}</Table>

Checkbox selection

Use TableCheckboxCell in the first cell of each TableBody row, and optionally TableHeaderCheckboxCell for the "select all".

Name
Type
Bulbasaur
Grass
Charmander
Fire
Squirtle
Water
<Table>
  <TableHeader>
    <TableRow>
      <TableHeaderCheckboxCell>Select</TableHeaderCheckboxCell>
      <TableHeaderCell>Name</TableHeaderCell>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCheckboxCell>001</TableCheckboxCell>
      <TableCell>Bulbasaur</TableCell>
    </TableRow>
  </TableBody>
</Table>

Pagination

The pagination bar is stateless: Table renders it, you own the page index and the data slice. Provide the four range props together (initialPagination, finalPagination, totalPagination, linesPerPage) to display the bar; wire the callbacks to make it navigable.

Navigable

onPreviousPage/onNextPage render the previous/next buttons; linesPerPageValue + onLinesPerPageChange control the rows-per-page selector. The buttons' enabled state is derived from a numeric range (previous while initialPagination > 1, next while finalPagination < totalPagination) — pass canPreviousPage/canNextPage to override it (formatted strings such as "1.234" keep both enabled).

Exibindo 1 - 3 de 10 itens
Linhas por página
3
No.
Name
Type
001
Bulbasaur
Grass
004
Charmander
Fire
007
Squirtle
Water
const [page, setPage] = React.useState(0);
const [pageSize, setPageSize] = React.useState(10);
const start = page * pageSize;
const rows = data.slice(start, start + pageSize); // or the server slice
 
<Table
  initialPagination={start + 1}
  finalPagination={Math.min(start + pageSize, data.length)}
  totalPagination={data.length}
  linesPerPage={[
    { label: '10', value: 10 },
    { label: '20', value: 20 },
    { label: '30', value: 30 },
  ]}
  linesPerPageValue={pageSize}
  onLinesPerPageChange={(size) => {
    setPageSize(size);
    setPage(0);
  }}
  onPreviousPage={() => setPage((current) => current - 1)}
  onNextPage={() => setPage((current) => current + 1)}
>
  {/* rows */}
</Table>;

Display-only

Without the callbacks the bar only shows the range and the selector (the historical behaviour):

Exibindo 1 - 3 de 30 itens
Linhas por página
Selecione...
No.
Name
Type
001
Bulbasaur
Grass
004
Charmander
Fire
007
Squirtle
Water
<Table
  initialPagination="1"
  finalPagination="10"
  totalPagination="30"
  linesPerPage={[
    { label: '10', value: 10 },
    { label: '20', value: 20 },
    { label: '30', value: 30 },
  ]}
>
  {/* ... */}
</Table>

Labels: showingText, linesPerPageLabel, previousLabel, nextLabel (pt-BR defaults) — see TablePagination.

Column totals

columnSum is the (0-based) index of the column to sum. Render an empty <TableFooter /> — it will be filled automatically with "Total: X":

SKU
Product
Qty
A-01
Shirt
3
A-02
Sneakers
1
A-03
Cap
5
 
Total:
<Table columnSum={4}>
  {/* ...header and body... */}
  <TableFooter />
</Table>

Sharing

Enable the share button (email/social network) with share:

<Table
  share={{
    email: () => sendByEmail(),
    social: () => shareOnSocial(),
  }}
>
  {/* ... */}
</Table>

Empty state

TableEmpty is rendered automatically when all rows are hidden by the search. For custom placeholders, assemble it manually:

No records

Try adjusting the filters.

import { TableEmpty } from '@apollion-dsi/core/data-display/table';
 
<TableEmpty titleEmpty="No records" subtitleEmpty="Try adjusting the filters." />;

See also

  • Storybook story: Components / Table
  • Full API: TableInterface (reference generated from TSDoc).
  • High-performance virtualized table: DataTable.