UIUpdated April 28, 2026

Table

Structured data table component with loading and empty states for PyColors UI.

UITable

Structured data for SaaS interfacesLink to section

The Table component displays structured, comparable data.

Use it for:

  • users
  • projects
  • invoices
  • subscriptions
  • logs
  • reports
  • audit events
  • admin records

Table provides a consistent visual foundation for dense product data:

  • semantic table structure
  • design-token styling
  • horizontal overflow handling
  • row hover feedback
  • built-in empty state
  • built-in loading state

Core idea

Use Table when users need to compare structured records across columns. Use cards when the content is more editorial, visual, or action-led.

ImportLink to section

table-import.tsx
import { Table } from "@pycolors/ui";

Basic usageLink to section

NameEmailRole
Jane Doejane@example.comAdmin
John Smithjohn@example.comUser
users-table.tsx
import {
  Table,
  TableHeader,
  TableBody,
  TableRow,
  TableHead,
  TableCell,
} from "@pycolors/ui";

export function UsersTable() {
  return (
    <Table>
      <TableHeader>
        <TableRow>
          <TableHead>Name</TableHead>
          <TableHead>Email</TableHead>
          <TableHead>Role</TableHead>
        </TableRow>
      </TableHeader>

      <TableBody>
        <TableRow>
          <TableCell>Jane Doe</TableCell>
          <TableCell>jane@example.com</TableCell>
          <TableCell>Admin</TableCell>
        </TableRow>
        <TableRow>
          <TableCell>John Smith</TableCell>
          <TableCell>john@example.com</TableCell>
          <TableCell>User</TableCell>
        </TableRow>
      </TableBody>
    </Table>
  );
}

When to use TableLink to section

Use Table when the interface needs scanning, comparison, and operational density.

Repeatable records

Use for users, projects, invoices, subscriptions, logs, and admin records.

Comparable data

Use when users compare values across stable columns.

Operational views

Use with filters, pagination, row actions, and bulk operations.

CaptionLink to section

Use TableCaption when the table needs a concise summary.

table-caption.tsx
<Table>
  <TableCaption>List of active users</TableCaption>
  {/* rows */}
</Table>

Captions should explain what the table contains, not repeat the page heading.

Empty stateLink to section

Use TableEmpty when the request succeeded but no rows are available.

table-empty.tsx
<TableBody>
  <TableEmpty colSpan={3} />
</TableBody>
table-empty-custom.tsx
<TableBody>
  <TableEmpty
    colSpan={3}
    title="No users found"
    description="Invite your first user to get started."
  />
</TableBody>

Empty state rule

Empty is not an error. Explain why the table is empty and what the user can do next.

Loading stateLink to section

Use TableLoading when the table structure is known but the data is still loading.

table-loading.tsx
<TableBody>
  <TableLoading colSpan={3} />
</TableBody>

Use loading rows instead of removing the table from the page.

This preserves layout and reduces visual jump.

Table compositionLink to section

A production table usually belongs inside a larger feature composition.

projects-table-section.tsx
export function ProjectsTableSection() {
  return (
    <section className="space-y-4">
      <ProjectsToolbar />
      <Table>
        <ProjectsTableHeader />
        <ProjectsTableBody />
      </Table>
      <ProjectsPagination />
    </section>
  );
}

The table renders structure.

The feature layer owns:

  • data fetching
  • filters
  • sorting
  • pagination
  • permissions
  • mutations
  • row actions

Architecture rule

The table should not own the data source. Keep fetching, filtering, sorting, and mutations in the feature layer.

Usage patternsLink to section

Define the columnsLink to section

Choose stable columns that help users scan and compare records.

Add loading and empty statesLink to section

Do not leave the table area blank while waiting for data or when no data exists.

Keep row actions compactLink to section

Use a Dropdown Menu for secondary row actions and a Dialog for destructive confirmation.

Add pagination when neededLink to section

Use Pagination when the data set is larger than a single page.

Move complex behavior into a patternLink to section

Use the Data Table Pattern when you need filters, actions, pagination, loading, empty, and error states together.

Table vs Card vs Data Table PatternLink to section

SituationUse
Structured comparable dataTable
Visual or content-heavy itemsCard
Small list with one primary action per itemCard or custom list
Filters, pagination, row actions, statesData Table Pattern
Timeline or activity feedCustom list
Mobile-first browsingCards or responsive list

Decision rule

Table is a primitive. Data Table Pattern is the full product experience around it.

APILink to section

ComponentsLink to section

ComponentDescription
TableWrapper with border and overflow handling
TableHeaderTable header section
TableBodyTable body section
TableRowTable row with hover feedback
TableHeadHeader cell
TableCellData cell
TableCaptionOptional caption
TableEmptyEmpty state row
TableLoadingLoading state row

TableEmpty propsLink to section

PropTypeDefaultDescription
colSpannumberNumber of columns to span
titlestringdefault copyEmpty state title
descriptionstringdefault copyEmpty state description
classNamestringCustom classes

TableLoading propsLink to section

PropTypeDefaultDescription
colSpannumberNumber of columns to span
labelstringdefault copyLoading message
classNamestringCustom classes

All base table components extend their respective native HTML element props.

table-props.ts
React.HTMLAttributes<HTMLTableElement>
React.HTMLAttributes<HTMLTableSectionElement>
React.HTMLAttributes<HTMLTableRowElement>
React.ThHTMLAttributes<HTMLTableCellElement>
React.TdHTMLAttributes<HTMLTableCellElement>
React.HTMLAttributes<HTMLTableCaptionElement>

AccessibilityLink to section

Tables should preserve semantic structure.

Guidelines:

  • use TableHead for column headers
  • include a caption when the context is not obvious
  • avoid using tables for layout
  • keep cells readable and concise
  • ensure interactive row actions are keyboard accessible
  • avoid making the whole row clickable unless the interaction is clear
  • use visible labels for icon-only row actions

Accessibility rule

Tables are for tabular data. Do not use table markup only to align content visually.

Prefer / avoidLink to section

Prefer

  • clear column headers
  • reasonable column count
  • empty and loading states
  • Dropdown Menu for row actions
  • Pagination for larger data sets

Avoid

  • tables for non-tabular content
  • too many columns in narrow layouts
  • blank states while loading
  • complex layouts inside cells
  • data fetching inside the table primitive

Product copy guidelinesLink to section

Table labels should be short and scannable.

Column headers

Prefer concise labels like Name, Email, Role, Status, Amount, Created, Actions.

Empty state

Explain the state and provide a next step: “No users found. Invite your first user to get started.”

Loading state

Keep the message neutral and short: “Loading data…” or “Loading users…”

Common questionsLink to section