Table
Zero-runtime semantic HTML data table primitives styled with StyleX, tailored for TanStack Table, TanStack Virtual, TanStack Query, TanStack Store, and TanStack Pacer.
Table
The Table component is a collection of composable, zero-runtime semantic HTML table primitives styled with StyleX. Built to pair seamlessly with headless data engines like TanStack Table, it provides accessible table markup, customizable visual variants (dense, striped, hoverable, stickyHeader), column pinning, sort indicators, numeric alignment, and built-in loading and empty states.
Preview
Service | Role | Status | Latency |
|---|---|---|---|
| Auth Gateway | Security | Operational | 24ms |
| Global Edge CDN | Edge | Degraded | 142ms |
| PostgreSQL Primary | Database | Operational | 4ms |
| Worker Queue | Compute | Operational | 18ms |
Properties
import {
Table,
TableHeader,
Column,
TableBody,
Row,
Cell,
Badge,
} from '@moul-dev/ui';
import { useState } from 'react';
export default function Example() {
const [sortDescriptor, setSortDescriptor] = useState({
column: 'name',
direction: 'ascending',
});
const services = [
{ id: '1', name: 'Auth Gateway', role: 'Security', status: 'Operational', variant: 'success' },
{ id: '2', name: 'PostgreSQL Primary', role: 'Database', status: 'Operational', variant: 'success' },
{ id: '3', name: 'Global Edge CDN', role: 'Edge', status: 'Degraded', variant: 'warning' },
{ id: '4', name: 'Worker Queue', role: 'Compute', status: 'Operational', variant: 'success' },
];
return (
<Table
aria-label="Services Table" selectionMode="undefined"
sortDescriptor={sortDescriptor}
onSortChange={setSortDescriptor}
>
<TableHeader>
<Column id="name" isRowHeader allowsSorting>Service</Column>
<Column id="role" allowsSorting>Role</Column>
<Column id="status" allowsSorting>Status</Column>
<Column id="latency" allowsSorting>Latency</Column>
</TableHeader>
<TableBody items={services}>
{(item) => (
<Row key={item.id} id={item.id}>
<Cell>{item.name}</Cell>
<Cell>{item.role}</Cell>
<Cell>
<Badge variant={item.variant} size="sm" dot>
{item.status}
</Badge>
</Cell>
<Cell>{item.latency}</Cell>
</Row>
)}
</TableBody>
</Table>
);
}Import
import {
Table,
TableHeader,
TableBody,
TableFooter,
TableRow,
TableHead,
TableCell,
TableCaption,
TableEmpty,
TableSkeleton,
} from '@moul-dev/ui';Interactive TanStack Table Showcase
Experience a full-featured data table powered by TanStack Table, TanStack Store (reactive view preferences), and TanStack Pacer (debounced search filtering):
Service Name | Team | Region | Health | CPU Load | Memory | Latency | Throughput | Uptime | Action | |
|---|---|---|---|---|---|---|---|---|---|---|
Auth Gatewaysrv-1 | Security | us-east-1 | Operational | 18% | 1.4 GB | 24ms | 1.2M req/s | 99.99% | ||
Edge CDN Gatewaysrv-3 | Networking | global | Degraded | 78% | 3.8 GB | 142ms | 3.8M req/s | 99.85% | ||
Media Transcodersrv-7 | Media | us-east-2 | Operational | 64% | 5.2 GB | 85ms | 140k req/s | 99.90% | ||
Notification Hubsrv-8 | Messaging | eu-west-1 | Operational | 22% | 1.8 GB | 12ms | 2.1M req/s | 99.99% | ||
Payment Processingsrv-5 | Billing | us-east-1 | Incident | 94% | 8.6 GB | 520ms | 95k req/s | 98.90% | ||
| Showing 5 of 8 total services | Cluster Health: 100% Verified | |||||||||
Basic Semantic Usage
Construct a table declaratively with standard semantic primitives without any external state dependencies:
| Service Name | Region | Status | Throughput |
|---|---|---|---|
| Auth Gateway | us-east-1 | Operational | 1.2M req/s |
| PostgreSQL Primary | us-west-2 | Operational | 450k req/s |
| Edge CDN Gateway | global | Degraded | 3.8M req/s |
| Cluster Summary | 3 Nodes Active | ||
import {
Table,
TableHeader,
TableBody,
TableFooter,
TableRow,
TableHead,
TableCell,
TableCaption,
Badge,
} from '@moul-dev/ui';
export default function BasicTableExample() {
return (
<Table aria-label="Cluster Services Table">
<TableCaption>Active production services across clusters</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Service Name</TableHead>
<TableHead>Region</TableHead>
<TableHead>Status</TableHead>
<TableHead align="numeric">Throughput</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-semibold">Auth Gateway</TableCell>
<TableCell>us-east-1</TableCell>
<TableCell><Badge variant="success" size="sm" dot>Operational</Badge></TableCell>
<TableCell align="numeric">1.2M req/s</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-semibold">PostgreSQL Primary</TableCell>
<TableCell>us-west-2</TableCell>
<TableCell><Badge variant="success" size="sm" dot>Operational</Badge></TableCell>
<TableCell align="numeric">450k req/s</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-semibold">Edge CDN Gateway</TableCell>
<TableCell>global</TableCell>
<TableCell><Badge variant="warning" size="sm" dot>Degraded</Badge></TableCell>
<TableCell align="numeric">3.8M req/s</TableCell>
</TableRow>
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Cluster Summary</TableCell>
<TableCell align="numeric">3 Nodes Active</TableCell>
</TableRow>
</TableFooter>
</Table>
);
}Visual Variants
The Table component provides built-in visual variants for dense layouts, zebra striping, borders, and sticky headers:
// Compact / Dense layout for data-dense dashboards
<Table dense>...</Table>
// Zebra striping for alternating row backgrounds
<Table striped>...</Table>
// Sticky pinned thead while scrolling
<Table stickyHeader>...</Table>
// Fixed table column layout
<Table layout="fixed">...</Table>
// Sticky column pinning (left / right / start / end)
<TableHead pinned="left" pinOffset={0}>Pinned Service</TableHead>
<TableCell pinned="left" pinOffset={0}>Auth Service</TableCell>TanStack Table Integration
Connect @moul-dev/ui table primitives with @tanstack/react-table for multi-column sorting, row selection, filtering, and pagination:
import {
Table,
TableHeader,
TableBody,
TableRow,
TableHead,
TableCell,
Checkbox,
} from '@moul-dev/ui';
import {
useTable,
createColumnHelper,
createCoreRowModel,
createSortedRowModel,
createFilteredRowModel,
createPaginatedRowModel,
flexRender,
type SortingState,
type RowSelectionState,
} from '@tanstack/react-table';
import { useState, useMemo } from 'react';
interface Service {
id: string;
name: string;
region: string;
status: string;
latency: string;
}
const columnHelper = createColumnHelper<Service>();
export default function TanStackTableExample({ data }: { data: Service[] }) {
const [sorting, setSorting] = useState<SortingState>([]);
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const columns = useMemo(
() => [
columnHelper.display({
id: 'select',
header: ({ table }) => (
<Checkbox
aria-label="Select all"
isSelected={table.getIsAllPageRowsSelected()}
isIndeterminate={table.getIsSomePageRowsSelected()}
onChange={(val) => table.toggleAllPageRowsSelected(val)}
/>
),
cell: ({ row }) => (
<Checkbox
aria-label="Select row"
isSelected={row.getIsSelected()}
onChange={(val) => row.toggleSelected(val)}
/>
),
}),
columnHelper.accessor('name', {
header: 'Service Name',
cell: (info) => info.getValue(),
}),
columnHelper.accessor('region', {
header: 'Region',
cell: (info) => info.getValue(),
}),
columnHelper.accessor('status', {
header: 'Status',
cell: (info) => info.getValue(),
}),
columnHelper.accessor('latency', {
header: 'Latency',
cell: (info) => info.getValue(),
}),
],
[]
);
const table = useTable({
data,
columns,
state: { sorting, rowSelection },
onSortingChange: setSorting,
onRowSelectionChange: setRowSelection,
getCoreRowModel: createCoreRowModel(),
getSortedRowModel: createSortedRowModel(),
getFilteredRowModel: createFilteredRowModel(),
getPaginationRowModel: createPaginatedRowModel(),
});
return (
<Table stickyHeader>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead
key={header.id}
sortDirection={header.column.getIsSorted()}
onSort={
header.column.getCanSort()
? header.column.getToggleSortingHandler()
: undefined
}
>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows.map((row) => (
<TableRow key={row.id} selected={row.getIsSelected()}>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
);
}Large Dataset Virtualization (TanStack Virtual)
Render thousands of table rows with smooth 60fps windowing using @tanstack/react-virtual:
@tanstack/react-virtualDOM Rows in View: 0| Node Hostname | Private IP | CPU Load | Memory | Status |
|---|
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '@moul-dev/ui';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useRef } from 'react';
export function VirtualTableExample({ data }: { data: any[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const rowVirtualizer = useVirtualizer({
count: data.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 40,
overscan: 10,
});
const virtualRows = rowVirtualizer.getVirtualItems();
const totalSize = rowVirtualizer.getTotalSize();
const paddingTop = virtualRows.length > 0 ? virtualRows[0].start || 0 : 0;
const paddingBottom =
virtualRows.length > 0
? totalSize - (virtualRows[virtualRows.length - 1].end || 0)
: 0;
return (
<div ref={parentRef} style={{ maxHeight: '360px', overflowY: 'auto' }}>
<Table stickyHeader dense wrapInContainer={false}>
<TableHeader>
<TableRow>
<TableHead>Hostname</TableHead>
<TableHead>IP Address</TableHead>
<TableHead align="numeric">CPU</TableHead>
<TableHead>Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{paddingTop > 0 && (
<tr>
<td style={{ height: `${paddingTop}px` }} colSpan={4} />
</tr>
)}
{virtualRows.map((virtualRow) => {
const item = data[virtualRow.index];
return (
<TableRow key={item.id}>
<TableCell>{item.hostname}</TableCell>
<TableCell>{item.ip}</TableCell>
<TableCell align="numeric">{item.cpu}</TableCell>
<TableCell>{item.status}</TableCell>
</TableRow>
);
})}
{paddingBottom > 0 && (
<tr>
<td style={{ height: `${paddingBottom}px` }} colSpan={4} />
</tr>
)}
</TableBody>
</Table>
</div>
);
}Server-Side Pagination with TanStack Query & Pacer
Handle asynchronous server data fetching with cached background refetches and loading skeletons:
| Event ID | Timestamp | Level | Log Payload |
|---|---|---|---|
import {
Table,
TableHeader,
TableBody,
TableRow,
TableHead,
TableCell,
TableSkeleton,
Button,
} from '@moul-dev/ui';
import { useQuery } from '@tanstack/react-query';
import { useDebouncedValue } from '@tanstack/react-pacer';
import { useState } from 'react';
export function QueryTableExample() {
const [page, setPage] = useState(1);
const [search, setSearch] = useState('');
const [debouncedSearch] = useDebouncedValue(search, 300);
const { data, isFetching } = useQuery({
queryKey: ['cluster-nodes', page, debouncedSearch],
queryFn: () => fetchNodes({ page, search: debouncedSearch }),
});
return (
<Table dense>
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead>Node Name</TableHead>
<TableHead>Cluster</TableHead>
<TableHead align="numeric">Health Score</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{isFetching && !data ? (
<TableSkeleton rows={5} columns={4} />
) : (
data?.records.map((node: any) => (
<TableRow key={node.id}>
<TableCell>{node.id}</TableCell>
<TableCell>{node.name}</TableCell>
<TableCell>{node.cluster}</TableCell>
<TableCell align="numeric">{node.score}</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
);
}Loading and Empty State Components
Display animated skeleton pulse rows while data is fetching, or render custom empty states when queries return 0 results:
// 1. Loading Skeleton Rows
<TableBody>
<TableSkeleton rows={5} columns={4} />
</TableBody>
// 2. Empty State Row
<TableBody>
<TableEmpty colSpan={4}>
<EmptyState
title="No cluster nodes found"
description="Try clearing active query filters."
/>
</TableEmpty>
</TableBody>Props Reference
Table Props
Prop
Type
TableHeader Props
Prop
Type
TableRow Props
Prop
Type
TableHead Props
Prop
Type
TableCell Props
Prop
Type
TableEmpty Props
Prop
Type
TableSkeleton Props
Prop
Type