Table

Table

Overview

HudTable is the tabular-data primitive. Same conceptual API on both surfaces: items + column descriptors + optional selection + density. Apple ships HudTable as a SwiftUI view; web ships hudsonkit/table (TS) with web-native affordances the native side doesn't have — namely resizable columns with localStorage persistence.

v1 is presentational + selectable. Sorting, editing, and built-in detail-mode are deliberately deferred. Apps that need drill-down compose onSelect with NavigationStack / sheet / their own inspector.

Apple — HudTable (SwiftUI)

import HudsonUI

HudTable(agents, columns: [
    HudTableColumn("Name") { Text($0.name) },
    HudTableColumn("Status", alignment: .center) {
        HudBadge($0.status, tint: .green)
    },
    HudTableColumn("Updated", alignment: .trailing) { Text($0.updatedAt) },
]) { agent in
    selectedAgent = agent
}

Initializer

public init(
    _ items: [Item],
    columns: [HudTableColumn<Item>],
    density: HudTableDensity = .regular,
    selection: Binding<Item.ID?>? = nil,
    onSelect: ((Item) -> Void)? = nil
)
NameTypeDescription
items[Item] where Item: IdentifiableRow data.
columns[HudTableColumn<Item>]Column descriptors with title, alignment, cell builder.
densityHudTableDensity.compact (28pt) or .regular (44pt). Defaults to .regular.
selectionBinding<Item.ID?>?Optional external selection binding. If omitted, selection is held internally.
onSelect((Item) -> Void)?Fired on row tap.

HudTableColumn

HudTableColumn("Updated", alignment: .trailing) { agent in
    Text(agent.updatedAt).foregroundStyle(HudPalette.muted)
}

Cell builders receive the row item and return any View — including Hudson primitives like HudBadge, HudStatusDot, etc.

Density

CaseRow heightWhen to use
.compactHudLayout.rowHeightCompact (28pt)Dense data tables, log views.
.regularHudLayout.rowHeightRegular (44pt)Primary content, tap-target lists.

Tighter than the iOS List defaults so tables stay dense without losing legibility.

Web — hudsonkit/table

import { HudTable } from 'hudsonkit/table';

<HudTable
  items={agents}
  rowKey={(a) => a.id}
  selectedKey={selected}
  onSelect={(a) => setSelected(a.id)}
  storageKey="hudson:agents-table"
  columns={[
    { key: 'name',    title: 'Name',    defaultWidth: 240, cell: (a) => a.name },
    { key: 'status',  title: 'Status',  defaultWidth: 120, alignment: 'center',  cell: (a) => <StatusBadge of={a} /> },
    { key: 'updated', title: 'Updated', defaultWidth: 120, alignment: 'trailing', cell: (a) => a.updated },
  ]}
/>

HudTableProps<Item>

NameTypeDescription
itemsreadonly Item[]Row data.
columnsHudTableColumn<Item>[]Column descriptors.
rowKey(item: Item) => stringStable key per row — used for selection comparison and React keys.
density'compact' | 'regular'Defaults to 'regular'.
selectedKeystring | nullExternally controlled selection.
onSelect(item: Item) => voidFired on row click.
storageKeystringlocalStorage key for persisted column widths. Omit to skip persistence.

HudTableColumn<Item>

NameTypeDescription
keystringStable column id (used by the resize state).
titlestringHeader label.
alignment'leading' | 'center' | 'trailing'Defaults to 'leading'.
defaultWidthnumberInitial width in px. Defaults to 160.
minWidth / maxWidthnumberClamped during drag. Defaults: 48 / 720.
cell(item: Item) => ReactNodeCell renderer.

Resizable columns

Drag the right edge of any header to resize. Double-click resets to defaultWidth. Widths persist to localStorage under storageKey and survive remounts.

useResizableColumns is exported separately for tables that aren't built on HudTable:

import { useResizableColumns } from 'hudsonkit/table';

const { getColumnProps, getResizeHandleProps, resetAll } = useResizableColumns({
  storageKey: 'my-table',
  columns: [{ key: 'name', defaultWidth: 240 }, ...],
});

Use the hook when a custom table needs the same persistent resize behavior without adopting the full HudTable rendering.

Tokens consumed

Both surfaces read from the shared design system: surfaces from --hud-surface / HudPalette.surface, hairlines from --hud-border / HudHairline, selected-row tint from --hud-accent-soft / HudSurface.tintFill(HudPalette.accent), header type from the mono xxs size with HudPalette.dim.

For AI agents