EQTY Lab Equality

Table

A data table for displaying structured information in rows and columns.

View as Markdown

Overview


Tables are built from compositional primitives that map directly to HTML table elements . Each primitive is a styled wrapper that accepts all native HTML attributes, giving you full control over layout, sizing, and responsiveness.

  • TableContainer: Wraps the <table> in a scrollable container with elevation styling.
  • TableHeader / TableBody / TableFooter: Semantic section wrappers (<thead>, <tbody>, <tfoot>).
  • TableRow: A table row (<tr>) that accepts onClick for clickable rows.
  • TableHead: A column header cell (<th>) with a truncate prop for overflow control.
  • TableCell: A data cell (<td>) with a truncate prop for overflow control.

Usage


Import the components:

tsx
import {
  TableContainer,
  TableHeader,
  TableBody,
  TableRow,
  TableHead,
  TableCell,
} from "@eqtylab/equality";

Basic usage:

tsx
<TableContainer>
  <TableHeader>
    <TableRow>
      <TableHead>Name</TableHead>
      <TableHead>Email</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>Alice Cooper</TableCell>
      <TableCell>alice@example.com</TableCell>
    </TableRow>
  </TableBody>
</TableContainer>

Variants


Default

Clickable Rows

Rows accept an onClick handler, which enables hover interactions.

Sortable Columns

Use <SortButton> inside <TableHead> cells to add interactive sort controls.

Usage

tsx
import {
  SortButton,
  TableContainer,
  TableHeader,
  TableRow,
  TableHead,
} from "@eqtylab/equality";

<TableContainer>
  <TableHeader>
    <TableRow>
      <TableHead>
        <SortButton
          field="name"
          sortField={sortField}
          sortDirection={sortDirection}
          onSort={handleSort}
        >
          Name
        </SortButton>
      </TableHead>
    </TableRow>
  </TableHeader>
</TableContainer>;

With Border

Use the border prop on TableContainer to apply an elevation-aware border with rounded corners. This should be added most places the table is used, except when it lives within a container that already has a border.

Usage

tsx
<TableContainer border>
  <TableHeader>
    <TableRow>
      <TableHead>Name</TableHead>
      <TableHead>Email</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>{/* rows */}</TableBody>
</TableContainer>

Empty State

When there are no rows, render empty state content in a <TableCell> that spans all columns with colSpan.

Empty State with Custom Component

The empty state cell accepts any ReactNode, so you can use a custom component like EmptyTableState.

Use the sticky prop on <TableHeader> to keep column headers visible while scrolling. The height of the <TableContainer> must be constrained for this to work as expected.

Usage

tsx
<TableContainer className="max-h-[400px]">
  <TableHeader sticky>
    <TableRow>
      <TableHead>Name</TableHead>
      <TableHead>Email</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>{/* rows */}</TableBody>
</TableContainer>

Column Sizing


Use the tableLayout prop on TableContainer to control how column widths are calculated. Set explicit widths on <TableHead> cells using Tailwind classes.

Fixed Layout

With tableLayout="fixed", columns respect explicit widths exactly. This is the recommended approach when you need predictable column sizing.

Usage

tsx
<TableContainer tableLayout="fixed">
  <TableHeader>
    <TableRow>
      <TableHead className="w-[30%]">Name</TableHead>
      <TableHead className="w-[30%]">Email</TableHead>
      <TableHead className="w-[100px]">Role</TableHead>
      <TableHead className="w-[100px]">Status</TableHead>
      <TableHead className="w-[60px]" />
    </TableRow>
  </TableHeader>
</TableContainer>

Min and Max Width

Use min-w- or max-w- Tailwind classes on <TableHead> to constrain column sizes. min-w- works in both auto and fixed layouts. max-w- works best with the default auto layout.

tsx
{
  /* Column won't shrink below 150px */
}
<TableHead className="min-w-[150px]">Description</TableHead>;

{
  /* Column won't grow beyond 300px — pair with truncate */
}
<TableHead className="max-w-[300px]" truncate>
  Email
</TableHead>;

Shrink to Content

In the default auto layout, use w-[1%] to minimize a column to fit its content. The browser’s table algorithm ensures the column still renders at least as wide as its content, while giving all remaining space to other columns. This is useful for action columns or icon-only columns.

tsx
<TableHead className="w-[1%]">{/* Actions */}</TableHead>

Truncation


Use the truncate prop on <TableHead> and <TableCell> to clip overflowing text with an ellipsis. This works best with tableLayout="fixed" and an explicit column width so the cell has a defined boundary to truncate against.

Usage

tsx
<TableContainer tableLayout="fixed">
  <TableHeader>
    <TableRow>
      <TableHead className="w-[25%]">Name</TableHead>
      <TableHead className="w-[40%]" truncate>
        Email
      </TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>Alice Cooper</TableCell>
      <TableCell truncate>alice.cooper.very.long.email@example.com</TableCell>
    </TableRow>
  </TableBody>
</TableContainer>

Responsive Columns


Use container queries to show or hide columns based on the table’s container width. Wrap the table in a @container element and apply hidden @md:table-cell (or similar) to both the <TableHead> and <TableCell> for columns that should collapse.

Usage

tsx
<div className="@container">
  <TableContainer>
    <TableHeader>
      <TableRow>
        <TableHead>Name</TableHead>
        <TableHead className="hidden @md:table-cell">Email</TableHead>
        <TableHead className="hidden @lg:table-cell">Role</TableHead>
        <TableHead>Status</TableHead>
      </TableRow>
    </TableHeader>
    <TableBody>
      <TableRow>
        <TableCell>Alice Cooper</TableCell>
        <TableCell className="hidden @md:table-cell">
          alice@example.com
        </TableCell>
        <TableCell className="hidden @lg:table-cell">Admin</TableCell>
        <TableCell>
          <Badge variant="success">Active</Badge>
        </TableCell>
      </TableRow>
    </TableBody>
  </TableContainer>
</div>

Elevations


Sunken

Base (default)

Raised

Overlay

Props


TableContainer

NameDescriptionTypeDefaultRequired
elevationControls the shadow and background stylingsunken, base, raised, overlayraised
tableLayoutControls the CSS table-layout algorithmauto, fixedauto
borderApplies an elevation-aware border with rounded cornersbooleanfalse

TableHeader

NameDescriptionTypeDefaultRequired
stickyKeeps the header visible while the table scrollsbooleanfalse

TableRow

NameDescriptionTypeDefaultRequired
clickableApplies hover and cursor interaction stylesbooleanfalse

TableHead

NameDescriptionTypeDefaultRequired
truncateClips overflowing content with an ellipsisbooleanfalse

TableCell

NameDescriptionTypeDefaultRequired
truncateClips overflowing content with an ellipsisbooleanfalse