Components Data display

Table

Records in rows and columns: columns say what each shows, dataSource holds the records; pages, pinned columns, loading and empty states.

import { Table, SelectionBar, Pagination } from '@kition/tessera-ui'

When to use

  • Lists of orders, users, events: anything scanned and compared row by row.

When not to

  • A spreadsheet the person edits: that is a kit.

Examples

Pages, pinned column, tags
OrderCustomerPlanAmountStatus
ord_1040Ada LovelaceTeam$19.00paid
ord_1041Grace HopperPro$29.00pending
ord_1042Alan TuringPro$39.00refunded
ord_1043Katherine JohnsonTeam$49.00paid
ord_1044Ada LovelacePro$59.00pending
ord_1045Grace HopperPro$19.00refunded
ord_1046Alan TuringTeam$29.00paid
ord_1047Katherine JohnsonPro$39.00pending
table.tsx
import { Table, Tag, type TableColumn } from '@kition/tessera-ui'

type Order = { id: string; customer: string; plan: string; amount: number; status: 'paid' | 'pending' | 'refunded' }

const ORDERS: Order[] = Array.from({ length: 23 }, (_, index) => ({
  id: `ord_${1040 + index}`,
  customer: ['Ada Lovelace', 'Grace Hopper', 'Alan Turing', 'Katherine Johnson'][index % 4],
  plan: index % 3 ? 'Pro' : 'Team',
  amount: 19 + (index % 5) * 10,
  status: (['paid', 'pending', 'refunded'] as const)[index % 3],
}))

const TONES = { paid: 'success', pending: 'warning', refunded: 'neutral' } as const

const columns: TableColumn<Order>[] = [
  { key: 'id', title: 'Order', dataIndex: 'id', width: 120, fixed: 'left' },
  { key: 'customer', title: 'Customer', dataIndex: 'customer', ellipsis: true },
  { key: 'plan', title: 'Plan', dataIndex: 'plan', width: 90 },
  { key: 'amount', title: 'Amount', dataIndex: 'amount', align: 'right', width: 100, render: (amount: number) => `${amount}.00` },
  { key: 'status', title: 'Status', dataIndex: 'status', width: 110, render: (status: Order['status']) => <Tag tone={TONES[status]}>{status}</Tag> },
]

export default function Demo() {
  return <Table columns={columns} dataSource={ORDERS} rowKey="id" size="small" pagination={{ defaultPageSize: 8, showSizeChanger: true, pageSizeOptions: [8, 16, 32] }} />
}
Selection, row actions, SelectionBar

Row actions show while the pointer is over a row or focus is in it. SelectionBar floats at the bottom of the nearest positioned box; Escape in it clears the selection.

NameOwnerModifiedActions
Q4 plan.docxAdaToday 10:12
Backlog.ktableMeToday 09:30
Roadmap review.pptxGraceYesterday
Budget 2026.xlsxMeYesterday
User research.pdfAlanOct 28
table-selection.tsx
import { useState, type Key } from 'react'
import { Button, IconButton, SelectionBar, Table, type TableColumn } from '@kition/tessera-ui'
import { CopyIcon, DownloadIcon, EllipsisIcon, FolderInputIcon, Share2Icon, StarIcon, TrashIcon } from '@kition/tessera-icons/react'

type File = { id: string; name: string; owner: string; modified: string }

const FILES: File[] = [
  { id: 'f1', name: 'Q4 plan.docx', owner: 'Ada', modified: 'Today 10:12' },
  { id: 'f2', name: 'Backlog.ktable', owner: 'Me', modified: 'Today 09:30' },
  { id: 'f3', name: 'Roadmap review.pptx', owner: 'Grace', modified: 'Yesterday' },
  { id: 'f4', name: 'Budget 2026.xlsx', owner: 'Me', modified: 'Yesterday' },
  { id: 'f5', name: 'User research.pdf', owner: 'Alan', modified: 'Oct 28' },
]

const columns: TableColumn<File>[] = [
  { key: 'name', title: 'Name', dataIndex: 'name', ellipsis: true },
  { key: 'owner', title: 'Owner', dataIndex: 'owner', width: 110 },
  { key: 'modified', title: 'Modified', dataIndex: 'modified', width: 140 },
]

export default function Demo() {
  const [selected, setSelected] = useState<Key[]>(['f2'])
  return (
    <div style={{ position: 'relative', minHeight: 360 }}>
      <Table
        columns={columns}
        dataSource={FILES}
        rowKey="id"
        pagination={false}
        rowSelection={{ selectedKeys: selected, onChange: setSelected, rowLabel: (file) => `Select ${file.name}` }}
        rowActions={(file) => (
          <>
            <IconButton icon={Share2Icon} label={`Share ${file.name}`} />
            <IconButton icon={DownloadIcon} label={`Download ${file.name}`} />
            <IconButton icon={StarIcon} label={`Star ${file.name}`} />
            <IconButton icon={EllipsisIcon} label={`More for ${file.name}`} />
          </>
        )}
      />
      <SelectionBar count={selected.length} onClear={() => setSelected([])}>
        <Button variant="ghost" size="sm"><Share2Icon size={16} />Share</Button>
        <Button variant="ghost" size="sm"><DownloadIcon size={16} />Download</Button>
        <Button variant="ghost" size="sm"><FolderInputIcon size={16} />Move</Button>
        <Button variant="ghost" size="sm"><CopyIcon size={16} />Copy</Button>
        <Button variant="ghost" size="sm"><TrashIcon size={16} />Delete</Button>
      </SelectionBar>
    </div>
  )
}
Empty and loading
NameID
No API keys
Create a key to call the API from your own code.
NameID
Build serverkey_1
table-states.tsx
import { useState } from 'react'
import { Button, Empty, Table, type TableColumn } from '@kition/tessera-ui'

type Key = { id: string; name: string }
const columns: TableColumn<Key>[] = [
  { key: 'name', title: 'Name', dataIndex: 'name' },
  { key: 'id', title: 'ID', dataIndex: 'id', width: 160 },
]

export default function Demo() {
  const [loading, setLoading] = useState(false)
  return (
    <div style={{ display: 'grid', gap: 16 }}>
      <Table columns={columns} dataSource={[]} rowKey="id" emptyText={<Empty title="No API keys" description="Create a key to call the API from your own code."><Button size="sm" variant="primary">Create key</Button></Empty>} />
      <Button size="sm" onClick={() => { setLoading(true); setTimeout(() => setLoading(false), 1500) }}>Load for 1.5s</Button>
      <Table columns={columns} dataSource={[{ id: 'key_1', name: 'Build server' }]} rowKey="id" loading={loading} />
    </div>
  )
}

Props

Table

Records in rows and columns: columns say what each shows (dataIndex, render), dataSource holds the records. Pages through long lists, shows a spinner while loading and emptyText when empty, and can pin columns and the header.

PropTypeDefaultDescription
columnsrequiredreadonly TableColumn<T>[]—
dataSourcerequiredreadonly T[] | undefined—
rowKeyrequiredstring | ((record: T) => Key)—The field that identifies a record, or a function of it.
loadingboolean—
paginationTablePagination | false—Pages of defaultPageSize rows (10 by default); false shows every row.
size'small' | 'middle''middle'small: 36px rows for dense screens; middle (default): 44px.
emptyTextReactNode—Shown when there are no rows (an Empty).
stickyboolean—Keeps the header in view while the page scrolls.
scrollXnumber | string—The table's least width; it scrolls sideways in a narrower container.
onRow(record: T, index: number) => HTMLAttributes<HTMLTableRowElement>—
rowSelectionTableSelection<T>—Selects records with check boxes; selected rows are marked. Pair it with SelectionBar for their actions.
rowActions(record: T, index: number) => ReactNode—A record's actions (icon buttons, a menu), at the row's end while the pointer is over the row or focus is in it.
captionReactNode—Caption above the table, for its accessible name.
classNamestring—
testIdstring—

SelectionBar

Actions on the selected records, floating at the bottom of the nearest positioned box: the count, the actions, and a button that clears the selection. With Table's rowSelection, it replaces a row of links on every record.

PropTypeDefaultDescription
countrequirednumber—How many records are selected; with none the bar is not drawn.
childrenrequiredReactNode—The actions on the selection: Buttons (variant="ghost") with an icon and a word.
onClearrequired() => void—Clears the selection: the close button, and Escape while focus is in the bar.
classNamestring—
testIdstring—

Pagination

The pages of a long list: the range shown, page buttons, and an optional page size.

PropTypeDefaultDescription
pagerequirednumber—
pageSizerequirednumber—
totalrequirednumber—
onChangerequired(page: number, pageSize: number) => void—
pageSizeOptionsreadonly number[]—Offers these page sizes; without it the size is fixed.
classNamestring—