Table

Lockness UI Component

VIEW

DOCUMENTATION

Table

A responsive table component for displaying tabular data with support for sorting, striping, hover effects, and borders.

Installation

bash
deno run -A jsr:@lockness/ui add table

Usage

tsx
import { 
  Table, 
  TableHeader, 
  TableBody, 
  TableFooter,
  TableRow, 
  TableHead, 
  TableCell, 
  TableCaption 
} from '@lockness/ui/components'

<Table>
  <TableCaption>A list of your recent invoices.</TableCaption>
  <TableHeader>
    <TableRow>
      <TableHead class="w-25">Invoice</TableHead>
      <TableHead>Status</TableHead>
      <TableHead>Method</TableHead>
      <TableHead class="text-right">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell class="font-medium">INV001</TableCell>
      <TableCell>Paid</TableCell>
      <TableCell>Credit Card</TableCell>
      <TableCell class="text-right">$250.00</TableCell>
    </TableRow>
  </TableBody>
  <TableFooter>
    <TableRow>
      <TableCell colSpan={3}>Total</TableCell>
      <TableCell class="text-right">$2,500.00</TableCell>
    </TableRow>
  </TableFooter>
</Table>

With Options

tsx
<Table striped hoverable bordered>
    {/* ... */}
</Table>

Sortable Headers

tsx
<TableHead sortable sortDirection='asc' sortHref='/users?sort=name&dir=desc'>
    Name
</TableHead>

Clickable Rows

tsx
<TableRow href='/users/1'>
    <TableCell>John Doe</TableCell>
</TableRow>

Components

Table

The root table component with responsive scrolling wrapper.

PropTypeDefaultDescription
classstring-Additional CSS class names
stripedbooleanfalseAdd zebra-striping to table rows
hoverablebooleanfalseAdd hover effect to table rows
borderedbooleanfalseAdd borders on all sides of the table and cells
childrenunknown-Table content

TableHeader

Container for table header rows.

PropTypeDefaultDescription
classstring-Additional CSS class names
childrenunknown-Header rows

TableBody

Container for table body rows.

PropTypeDefaultDescription
classstring-Additional CSS class names
childrenunknown-Body rows

TableFooter

Container for table footer rows.

PropTypeDefaultDescription
classstring-Additional CSS class names
childrenunknown-Footer rows

TableRow

A table row with hover and selection states.

PropTypeDefaultDescription
classstring-Additional CSS class names
selectedbooleanfalseWhether the row is selected
hrefstring-Make row clickable with navigation URL (uses Unpoly)
childrenunknown-Row cells

TableHead

A table header cell with optional sorting.

PropTypeDefaultDescription
classstring-Additional CSS class names
sortablebooleanfalseEnable sorting (adds visual indicator)
sortDirection'asc' | 'desc' | nullnullCurrent sort direction
sortHrefstring-Sort URL for Unpoly navigation
childrenunknown-Header content

TableCell

A table data cell.

PropTypeDefaultDescription
classstring-Additional CSS class names
childrenunknown-Cell content

TableCaption

A table caption for accessibility.

PropTypeDefaultDescription
classstring-Additional CSS class names
childrenunknown-Caption content

Theming

The Table component can be customized using CSS variables. This allows you to change the appearance of tables globally or override specific instances.

Available CSS Variables

VariableDefaultDescription
--table-padding-x0.7remHorizontal padding for cells
--table-padding-y0.7remVertical padding for cells
--table-header-height2.5remHeight of header cells
--table-row-height2.5remHeight of body rows
--table-font-size0.875remFont size for table text
--table-header-font-weight500Font weight for header cells
--table-border-radiusvar(--radius)Border radius for bordered tables
--table-border-colorvar(--border)Border color for table cells
--table-header-backgroundvar(--muted)Background color for header
--table-header-foregroundvar(--muted-foreground)Text color for header cells
--table-row-hover-backgroundvar(--muted)Background color on row hover
--table-row-stripe-backgroundvar(--muted)Background color for striped rows

Theming Examples

Global Customization

Customize all tables by setting CSS variables in your theme:

css
/* app/view/assets/app.css */
@theme {
    /* Make tables more spacious */
    --table-padding-x: 1.5rem;
    --table-padding-y: 1rem;
    --table-row-height: 3rem;

    /* Increase font size */
    --table-font-size: 1rem;
    --table-header-font-weight: 600;

    /* Customize colors */
    --table-header-background: hsl(210 40% 96%);
    --table-row-hover-background: hsl(210 40% 98%);
}

Local Overrides

Override CSS variables for specific table instances:

tsx
<div style="--table-padding-x: 0.5rem; --table-padding-y: 0.5rem;">
    <Table>
        {/* Compact table with less padding */}
    </Table>
</div>

<div style="--table-font-size: 1.125rem; --table-row-height: 4rem;">
    <Table>
        {/* Large table for better readability */}
    </Table>
</div>

Component-Specific Theming

Create themed sections with different table styles:

tsx
<div class="data-tables" style={{
    '--table-header-background': 'hsl(var(--primary))',
    '--table-header-foreground': 'hsl(var(--primary-foreground))',
    '--table-border-radius': '0.75rem'
}}>
    <Table bordered>
        {/* Tables with primary-colored headers */}
    </Table>
</div>

<div class="compact-tables" style={{
    '--table-padding-x': '0.75rem',
    '--table-padding-y': '0.5rem',
    '--table-row-height': '2rem',
    '--table-font-size': '0.8125rem'
}}>
    <Table striped>
        {/* Compact tables for dense data */}
    </Table>
</div>

Features

  • Responsive - Horizontal scrolling on overflow
  • Striped rows - Zebra striping for better readability
  • Hoverable - Row highlight on hover
  • Bordered - Full cell borders option
  • Sortable - Column sorting with Unpoly navigation
  • Clickable rows - Navigate on row click with Unpoly
  • Selection state - Visual indication for selected rows

BASIC TABLE

A list of your recent invoices.
InvoiceStatusMethodAmount
INV001PaidCredit Card$250.00
INV002PendingPayPal$150.00
INV003UnpaidBank Transfer$350.00
INV004PaidCredit Card$450.00
INV005PaidPayPal$550.00
Total$1,750.00
tsx
<Table>
  <TableCaption>A list of your recent invoices.</TableCaption>
  <TableHeader>
    <TableRow>
      <TableHead class="w-25">Invoice</TableHead>
      <TableHead>Status</TableHead>
      <TableHead>Method</TableHead>
      <TableHead class="text-right">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    {invoices.map((invoice) => (
      <TableRow key={invoice.id}>
        <TableCell class="font-medium">{invoice.id}</TableCell>
        <TableCell>{invoice.status}</TableCell>
        <TableCell>{invoice.method}</TableCell>
        <TableCell class="text-right">{invoice.amount}</TableCell>
      </TableRow>
    ))}
  </TableBody>
  <TableFooter>
    <TableRow>
      <TableCell colSpan={3}>Total</TableCell>
      <TableCell class="text-right">$1,750.00</TableCell>
    </TableRow>
  </TableFooter>
</Table>

CLICKABLE ROWS

IDStatusEmailAmount
728ed52fsuccessm@example.com$100.00
489e1d42processinguser@gmail.com$125.00
a1b2c3d4pendingtest@test.com$200.00
e5f6g7h8failedfail@example.com$75.00
tsx
<TableRow href={`/payments/${payment.id}`}>
  <TableCell>{payment.id}</TableCell>
  <TableCell>{payment.status}</TableCell>
  <TableCell>{payment.email}</TableCell>
  <TableCell class="text-right">${payment.amount}</TableCell>
</TableRow>

STRIPED ROWS

NameAgeAddressAction
John Brown45New York No. 1 Lake Park
Jim Green27London No. 1 Lake Park
Joe Black31Sidney No. 1 Lake Park
tsx
<Table striped>
  <TableHeader>
    <TableRow>
      <TableHead>Name</TableHead>
      <TableHead>Age</TableHead>
      <TableHead>Address</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>John Brown</TableCell>
      <TableCell>45</TableCell>
      <TableCell>New York No. 1 Lake Park</TableCell>
    </TableRow>
  </TableBody>
</Table>

SORTABLE HEADERS

IDEmailAmount
728ed52fm@example.com$100.00
489e1d42user@gmail.com$125.00
a1b2c3d4test@test.com$200.00
tsx
<TableHead 
  sortable 
  sortDirection="asc" 
  sortHref="?sort=id&dir=desc"
>
  ID
</TableHead>

<TableHead 
  sortable 
  sortDirection={null}  // unsorted
  sortHref="?sort=email&dir=asc"
>
  Email
</TableHead>

<TableHead 
  sortable 
  sortDirection="desc" 
  sortHref="?sort=amount&dir=asc"
>
  Amount
</TableHead>

EMPTY STATE

InvoiceStatusMethodAmount
No invoices found.
tsx
<TableBody>
  {data.length ? (
    data.map((item) => (
      <TableRow key={item.id}>
        {/* ... */}
      </TableRow>
    ))
  ) : (
    <TableEmpty colSpan={4}>No invoices found.</TableEmpty>
  )}
</TableBody>

SELECTED ROWS

IDEmailAmount
728ed52fm@example.com$100.00
489e1d42user@gmail.com$125.00
a1b2c3d4test@test.com$200.00
e5f6g7h8fail@example.com$75.00
tsx
<TableRow selected={isSelected}>
  <TableCell>{payment.id}</TableCell>
  <TableCell>{payment.email}</TableCell>
  <TableCell class="text-right">${payment.amount}</TableCell>
</TableRow>