Skip to content

Table

Semantic data-table primitives that render real table/th scope/aria-sort semantics on Web.

import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from '@beemvp/beeui-ui';

There is no documented deep/private source import. For source ownership from a BeeUI checkout:

Terminal window
pnpm beeui -- add table

Registry metadata: registry/registry.json.

  • Family exports: Table TableBody TableCaption TableCell TableFooter TableHead TableHeader TableRow

Exported types: TableBodyProps, TableCaptionProps, TableCellProps, TableFooterProps, TableHeaderProps, TableHeadProps, TableLayout, TableProps, TableRowProps, TableSortDirection

The generated API inventory is mechanically joined to packages/ui/src/index.ts, Registry metadata, and the component reference contract. For behavior details and defaults, use the canonical component behavior catalog rather than copying TypeScript declarations into a second hand-maintained table.

Controlled/uncontrolled props, callbacks, disabled semantics, normalization/fail-safe behavior, and mount/unmount rules are defined by the public types and the canonical behavior catalog. The executable fixtures below are the source-grounded usage examples; consumers should not infer state ownership from DOM structure or another UI library.

  • No additional provider is required by this family. BeeUIProvider remains the recommended application root.
  • Peer/native dependencies visible to this Registry item: react, react-native
  • Registry dependency closure: core-cn, text, theme, use-direction, use-required-callback-warning
  • Safe-area ownership remains explicit: shell surfaces touching system edges opt into SafeArea; components do not silently invent app-shell insets.
  • Web consumers load the BeeUI semantic theme CSS as documented in Web onboarding.

This family has platform-split source files. The bundler selects the native/Web implementation; do not infer native runtime behavior from the Web preview.

  • Web: live browser/keyboard behavior is verified by Web-specific checks where applicable.
  • iOS / Android: package/export/native compile evidence is not described as device-runtime proof. Consult the compatibility and native-preview guides for the exact evidence class.
  • Platform-specific or experimental behavior is called out in the canonical component/compatibility docs rather than hidden behind a generic parity claim.

Use the Accessibility overview, RTL/localization, and Large text & zoom alongside this family. Roles/states, keyboard/focus behavior, announcements, Dynamic Type/Web zoom, RTL, and reduced-motion expectations remain component-specific; BeeUI does not claim universal accessibility certification from automated tests.

BeeUI components consume semantic tokens and support the current typed variant/density contracts. Use Theming and Density. className is an implementation escape hatch for source-owned/application work, not a cross-engine portability guarantee.

Open the matching Web runtime in Showcase. The Showcase link demonstrates Web behavior; use the native-preview guide for real simulator/emulator/device paths.

This frame loads the real BeeUI Web Showcase on demand; it is not a second docs-only implementation. It proves browser behavior only. Use native preview for iOS/Android simulator, emulator or device paths.

  • Family root / primary export: Table
    • Public composition parts / helpers:
      • TableBody
      • TableCaption
      • TableCell
      • TableFooter
      • TableHead
      • TableHeader
      • TableRow
    • Exported type surface:
      • TableBodyProps
      • TableCaptionProps
      • TableCellProps
      • TableFooterProps
      • TableHeaderProps
      • TableHeadProps
      • TableLayout
      • TableProps
      • TableRowProps
      • TableSortDirection

The tree above is ordinary document structure so it remains readable with keyboard and assistive technology; it is derived from the real public export family rather than a canvas-only diagram.

The following is the exact typechecked runtime Showcase fixture selected for this live preview: apps/showcase/component-gallery/table-showcase.tsx. Runtime gallery/pattern sources are preferred over test harnesses, and the displayed source and executable source are the same file; there is no separately maintained demo snippet.

import {
Badge,
Card,
Checkbox,
IconButton,
Section,
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
Text,
VStack,
type TableSortDirection,
} from '@beemvp/beeui-ui';
import * as React from 'react';
type TeamMember = {
id: string;
name: string;
role: string;
status: 'Active' | 'Invited';
};
const TEAM_MEMBERS: TeamMember[] = [
{ id: 'ada', name: 'Ada Lovelace', role: 'Engineering', status: 'Active' },
{ id: 'grace', name: 'Grace Hopper', role: 'Engineering', status: 'Active' },
{ id: 'alan', name: 'Alan Turing', role: 'Research', status: 'Invited' },
];
// Real, caller-owned sort — proves `TableHead`'s `sortDirection`/`onSortChange`
// contract drives actual row order rather than a purely decorative indicator
// (ADR-007 "State boundaries": Table stores no sort state itself).
function sortByName(rows: TeamMember[], direction: TableSortDirection): TeamMember[] {
if (direction === 'none') return rows;
const sorted = [...rows].sort((a, b) => a.name.localeCompare(b.name));
return direction === 'descending' ? sorted.reverse() : sorted;
}
function nextSortDirection(current: TableSortDirection): TableSortDirection {
if (current === 'none') return 'ascending';
if (current === 'ascending') return 'descending';
return 'none';
}
function TeamTable({ layout }: { layout?: 'scroll' | 'stacked' }) {
const [sortDirection, setSortDirection] = React.useState<TableSortDirection>('none');
const [selectedIds, setSelectedIds] = React.useState<Set<string>>(new Set());
const rows = React.useMemo(() => sortByName(TEAM_MEMBERS, sortDirection), [sortDirection]);
const allSelected = selectedIds.size === TEAM_MEMBERS.length;
const someSelected = selectedIds.size > 0 && !allSelected;
const setRowSelected = (id: string, selected: boolean) => {
setSelectedIds((previous) => {
const next = new Set(previous);
if (selected) next.add(id);
else next.delete(id);
return next;
});
};
return (
<VStack gap="sm">
<Table layout={layout}>
<TableCaption>Team members</TableCaption>
<TableHeader>
<TableRow>
<TableHead label="Select all">
<Checkbox
accessibilityLabel="Select all team members"
checked={allSelected ? true : someSelected ? 'indeterminate' : false}
onCheckedChange={(checked) =>
setSelectedIds(checked ? new Set(TEAM_MEMBERS.map((member) => member.id)) : new Set())
}
/>
</TableHead>
<TableHead onSortChange={() => setSortDirection(nextSortDirection(sortDirection))} sortDirection={sortDirection}>
Name
</TableHead>
<TableHead>Role</TableHead>
<TableHead>Status</TableHead>
<TableHead label="Actions">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.map((member) => (
<TableRow key={member.id} selected={selectedIds.has(member.id)}>
<TableCell label="Select">
<Checkbox
accessibilityLabel={`Select ${member.name}`}
checked={selectedIds.has(member.id)}
onCheckedChange={(checked) => setRowSelected(member.id, checked)}
/>
</TableCell>
<TableCell>{member.name}</TableCell>
<TableCell>{member.role}</TableCell>
<TableCell>
<Badge variant={member.status === 'Active' ? 'success' : 'secondary'}>
{member.status}
</Badge>
</TableCell>
<TableCell label="Actions">
<IconButton accessibilityLabel={`Edit ${member.name}`} variant="ghost">
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<Text tone="muted" variant="caption">
{`Sort: ${sortDirection} · Selected: ${selectedIds.size}`}
</Text>
</VStack>
);
}
export function TableShowcase() {
return (
<VStack gap="lg">
<Card className="gap-4" testID="table-showcase" variant="raised">
<Section
description="Real HTML table/th-scope semantics, a keyboard-reachable sort trigger in normal tab order, and caller-owned row selection via Checkbox (ADR-007)."
title="Sortable, selectable table"
>
<TeamTable />
</Section>
</Card>
<Card className="gap-4" testID="table-showcase-stacked">
<Section
description="Explicit opt-in card/label-value presentation for narrow viewports — the same composed rows, no duplicated `.map()` loop."
title="Stacked layout"
>
<TeamTable layout="stacked" />
</Section>
</Card>
</VStack>
);
}

Use the code block’s copy affordance to copy the exact fixture. For a smaller app-specific example, start from the public imports shown above and keep only the state your screen owns.

No component-specific limitation is curated here. Check Compatibility and the linked behavior contract for target-specific constraints.

Implementation note: Platform-split; see docs/data-typography.md for data-cell typography.