Tessina UI
ConfiguratorGitHubIntroduction
InstallationUsageTheming
ComponentsAccordionAction SheetAlertAlertDialogArea ChartAspectRatioAvatarBadgeBannerBar ChartBottom NavBreadcrumbButtonButtonGroupCalendarCardCarouselChartChatBubbleCheckboxChipCodeBlockCollapsibleColor PickerComboboxCommandContainerContextMenuDate PickerDividerDrawerDropdown MenuEmptyStateFABFieldFieldsetFile UploadFile Upload 2FlexFormGridHoverCardIconButtonLabelLine ChartLinkMenubarMeterModalNavigation MenuNumberFieldOTP InputPaginationPie ChartPopoverProgressPromptInputRadar ChartRadial ChartRadioRatingScroll AreaSearchSegmentedControlSelectShortcutSidebarSkeletonSliderSpacerSpinnerSplit ButtonStackStatusStepperSurfaceSwitchTableTabsTextareaTime PickerToastToggleButtonToggleGroupToolbarTooltipTop Header DesktopTop Header Mobile
Contributing
Components

Table

A complete primitive set for tabular data — header, body, footer, sortable columns, sticky header, row states, plus cell helpers for avatars, status, currency amounts, and actions. Designed in the Wise / PrimeReact tradition.

Playground

Installation

pnpm add @tessinaui/ui

Usage

import {
  TableRoot,
  TableHeader,
  TableBody,
  TableFooter,
  TableRow,
  TableHead,
  TableCell,
  TableCaption,
  TableEmpty,
  TableCellAvatar,
  TableCellAmount,
  TableCellStatus,
  TableCellActions,
} from "@tessinaui/ui";
<TableRoot variant="lined" hoverable>
  <TableHeader>
    <TableRow hoverable={false}>
      <TableHead sortable sortDirection="asc" onSort={handleSort("name")}>User</TableHead>
      <TableHead>Role</TableHead>
      <TableHead>Status</TableHead>
      <TableHead numeric>Amount</TableHead>
      <TableHead align="end" />
    </TableRow>
  </TableHeader>

  <TableBody>
    {users.map((u) => (
      <TableRow key={u.id}>
        <TableCell>
          <TableCellAvatar
            avatar={<Avatar size="sm" initials={initialsOf(u.name)} />}
            name={u.name}
            secondary={u.email}
          />
        </TableCell>
        <TableCell>{u.role}</TableCell>
        <TableCell>
          <TableCellStatus intent="success" label="Active" />
        </TableCell>
        <TableCell numeric>
          <TableCellAmount currency="$" amount={u.amount.toFixed(2)} />
        </TableCell>
        <TableCell align="end">
          <TableCellActions>
            <IconButton variant="ghost" size="xs" icon={<MoreHorizontal />} aria-label="Actions" />
          </TableCellActions>
        </TableCell>
      </TableRow>
    ))}
  </TableBody>
</TableRoot>

Examples

Default

A team table composing avatar, status, currency, and action cell helpers.

Variants

Four visual treatments — lined, striped, bordered, and borderless.

Sizes

Five density steps from xs to xl scale cell padding and text size.

Row states

Rows can be selected, disabled, or destructive.

Sortable columns

Sortable headers cycle through none → asc → desc and re-sort the rows.

Empty & loading

TableEmpty fills the body when there's no data; TableSkeleton covers the loading state.

When to use

Table is the workhorse for any structured tabular data — users, transactions, orders, audit logs, settings rows, etc. It's a primitive set: bring your own data fetching, pagination, and selection state, and Tessina handles the visual layer plus sorting indicators, sticky header, and the cell patterns that show up everywhere.

For a turnkey component with built-in filtering / pagination / selection, layer TanStack Table on top of these primitives — the styling will carry through unchanged.

API Reference

TableRoot props

PropTypeDefaultDescription
size"xs" | "sm" | "md" | "lg" | "xl""md"Cell padding + text size
variant"lined" | "striped" | "bordered" | "borderless""lined"Visual treatment — lined is default, striped adds zebra rows, bordered adds vertical separators, borderless removes the container chrome
rounded"none" | "sm" | "md" | "lg" | "full""md"Container corner radius
intent"none" | "primary" | "error" | "warning" | "success" | "info""none"Subtle border accent on the container
dir"ltr" | "rtl""ltr"Text direction — flips alignment and sticky-column side
hoverablebooleantrueHighlight rows on hover
stickyHeaderbooleanfalseStick the header to the top of the scroll container
withContainerbooleantrueWrap the <table> in a TableContainer (you can disable to use your own)

TableContainer props

A standalone scroll container — useful if you need to wrap a table you didn't build with TableRoot.

PropTypeDefaultDescription
size, variant, rounded, intentsame as Root—Visual chrome
scrollablebooleantrueAdd horizontal overflow scrolling

TableHeader / TableBody / TableFooter

Semantic <thead> / <tbody> / <tfoot> wrappers. Header gets the muted background; footer gets the same styling for totals rows.

TableRow props

PropTypeDefaultDescription
selectedbooleanfalseHighlights the row in the primary tint
disabledbooleanfalseGreys out the row and disables pointer events
destructivebooleanfalseTints the row in error colour (e.g. flagged for deletion)
hoverablebooleaninheritedOverride the parent's hoverable setting for this row

TableHead props

PropTypeDefaultDescription
align"start" | "center" | "end""start"Text alignment
numericbooleanfalseRight-align with tabular numerals
sortablebooleanfalseShow a sort indicator and accept clicks to cycle
sortDirection"asc" | "desc" | "none""none"Current sort direction (controlled)
onSort(next: SortDirection) => void—Called when the user clicks the indicator. Receives the next direction in the cycle (none → asc → desc → none)
stickybooleanfalseStick this column to the start (left in LTR, right in RTL)
widthnumber | string—Column width hint

Sets aria-sort automatically when sortable is true.

TableCell props

Same align, numeric, sticky, width props as TableHead, plus truncate for ellipsis overflow.

TableEmpty props

PropTypeDefaultDescription
colSpannumberrequiredNumber of columns in the table — required so the cell spans correctly
iconReactNode—Optional icon shown above the message
titleReactNode—Bold title above the body text

Renders a single row with one cell that spans the table.

Cell helpers

These compose inside TableCell and cover the patterns that show up in nearly every product table.

TableCellAvatar

<TableCell>
  <TableCellAvatar
    avatar={<Avatar src={user.photo} initials={initialsOf(user.name)} />}
    name={user.name}
    secondary={user.email}
  />
</TableCell>
PropDescription
avatarSlot for any avatar component
namePrimary text (bold)
secondaryOptional second line (muted)

TableCellAmount

Right-aligns a currency amount with optional comparison delta (tinted by intent).

<TableCell numeric>
  <TableCellAmount
    currency="$"
    amount="1,240.50"
    delta="+12.4%"
    deltaIntent="success"
  />
</TableCell>
PropDescription
amountMain number (tabular numerals)
currencyOptional symbol or code shown before
deltaOptional comparison line below
deltaIntent"none" | "success" | "error"

TableCellStatus

Dot + label, in 6 intents (neutral, primary, error, warning, success, info).

<TableCell>
  <TableCellStatus intent="success" label="Active" />
</TableCell>

TableCellActions

A trailing-edge container for icon-button actions.

<TableCell align="end">
  <TableCellActions>
    <IconButton variant="ghost" size="xs" icon={<Pencil />} aria-label="Edit" />
    <IconButton variant="ghost" size="xs" icon={<Trash />} aria-label="Delete" />
  </TableCellActions>
</TableCell>

TableCellText

Two-line cell with primary + muted secondary.

<TableCell>
  <TableCellText primary="Wire transfer" secondary="ACH · 2 days" />
</TableCell>

Notes

  • The <table> element renders inside a TableContainer by default, which provides the rounded card chrome, intent border, and horizontal scroll for narrow viewports. Pass withContainer={false} if you want to nest the table inside your own container (e.g. a Card).
  • aria-sort="ascending" | "descending" | "none" is set automatically on sortable headers.
  • For huge datasets, virtualize the body with TanStack Virtual or a similar library — the primitives don't impose any virtualization but compose naturally with one.
  • For RTL: alignment, sortable indicators, and sticky-column side all flip via the dir prop. Numbers and currency stay LTR-formatted (which is the typographic norm even in Arabic-script copy).

Switch

A toggle control that allows users to turn a setting on or off

Tabs

Organise content into switchable panels with animated indicators, four variants, five sizes, five intents, icon/badge/closable support, vertical orientation, and full RTL/keyboard navigation.

On this page

PlaygroundInstallationUsageExamplesDefaultVariantsSizesRow statesSortable columnsEmpty & loadingWhen to useAPI ReferenceTableRoot propsTableContainer propsTableHeader / TableBody / TableFooterTableRow propsTableHead propsTableCell propsTableEmpty propsCell helpersTableCellAvatarTableCellAmountTableCellStatusTableCellActionsTableCellTextNotes