Tables display a set of data in an organized tabular format.

Table

Usage

  • Use Table to display sets of data in an organized, tabular format on a read-only basis.
  • Prefer Table for the most basic data sets — it is less opinionated and less custom than DataTable, and suitable for basic needs.
  • Use DataTable instead when the data needs sorting, filtering, pagination, or column management. Table cannot handle those interactions.
  • Never use Table for layout purposes. Use Box or Grid to arrange elements on a page.
  • Give every Table the three required parts: the container (Table), column headers (TableTh), and rows (TableTr).

Appearance

  • Auto layout (layout="auto", default) — columns adjust to fit their content. Use for most Tables.
  • Fixed layout (layout="fixed") — columns divide equally regardless of content. Use for predictable sizing when all columns carry similar amounts of data.
  • Default rows (no prop) — rows share a consistent background with a border separating each entry. Use for most cases.
  • Borderless rows (isBorderless) — removes the separator between entries for a cleaner presentation. Best for smaller data sets.
  • Striped rows (isStriped) — alternates between two background colors. Use for data-dense Tables with many rows of similarly structured data.

Behaviors

  • Base state — the resting state, with bordered rows and a subtle header background. Applied when no other state prop is set.
  • Header hover — clickable column headers change background and text color to show they can be activated. Applied automatically once the header is clickable.
  • Header focus — focused headers receive the standard 2px light blue outline. Applied automatically.
  • Active header (isActive on TableTh) — highlights a column header in the accent color. Use it to indicate the current sort column.
  • Row hover highlighting (shouldHighlightOnHover) — rows change background color on mouse hover to indicate interactivity. Off by default.
  • Depth padding (withDepthPadding on TableTd) — adds inline-start padding calculated from a nesting level, indenting child rows to convey hierarchy in tree-like or nested tables. Apply it to the first cell of each child row.
  • Panel alignment — inside a Panel, the edge cells (first and last in each row) inherit the Panel's content padding, so Table content aligns flush with the Panel's borders. Applied automatically; never add manual padding for this.

Content

  • Provide a caption on every Table — assistive technologies rely on it for the Table's purpose and the type of data it contains.
  • Write captions as short, descriptive nouns or noun phrases (e.g. "Product inventory"). Never write full sentences or redundant descriptions.
  • Write column headers that clearly label the data in each column.
  • Use sentence case for column headers.
  • Write out full words in column headers. Avoid abbreviations.

Implementation Notes

  • Table is composed from subcomponents, with no single-component API: Table wraps TableThead, TableTbody, and TableTfoot; each of those wraps TableTr; rows hold TableTh and TableTd.
  • caption is required on Table. Both frameworks throw at render when it is missing or an empty string. It renders as a visually hidden <caption>.
  • Pass onClick to TableTh to make a header clickable — the component renders an internal <button> for it. Never wrap header content in your own button.
  • TableTh sets its scope attribute from its parent context: col inside TableThead or TableTfoot, row inside TableTbody. A TableTh inside TableTbody also takes cell styling rather than header styling.
  • shouldHighlightOnHover and isBorderless exist on both Table and TableTr. Set them on TableTr to apply the behavior to individual rows.
  • The nesting level behind withDepthPadding comes from the tableRowDepth CSS variable (vars.tableRowDepth from @customerio/pluma-components/css), assigned as an inline style on the row — there is no depth prop, despite the design doc's wording. Padding resolves to (depth + 1) × the cell's inline-start padding, and depth defaults to 1 when the variable is unset.
  • Table and every subcomponent extend Box, accept its layout and sprinkle props, and are polymorphic via as.
  • Ember: components are prefixed with Pluma (<PlumaTable>, <PlumaTableThead>, <PlumaTableTbody>, <PlumaTableTfoot>, <PlumaTableTr>, <PlumaTableTh>, <PlumaTableTd>). Props use @arg syntax (@caption, @layout, @isStriped), and content is passed via {{yield}} (default block).

On this page