Table
Wrapper estilizado em volta da table HTML nativa. Apenas skin - traga o TanStack Table para ordenação, filtragem e paginação.
Visão geral
Table é intencionalmente um skin, não um comportamento. Oito peças - Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption - renderizam como table, thead, tbody, tfoot, tr, th, td e caption nativos com padding, bordas e hover consistentes. A semântica nativa permanece intacta, então leitores de tela e tecnologia assistiva tratam a marcação como uma table de dados real.
Para ordenação, filtragem, paginação, seleção de linha ou virtualização, componha Table com @tanstack/react-table (o padrão DataTable na camada de Patterns). O primitive em si nunca tenta ser um data grid - esse lock-in vive na camada de pattern.
Preview
| Item | Category | Deps |
|---|---|---|
| rx-message | AI | 4 |
| rx-button | Forms | 1 |
| rx-card | Display | 0 |
| rx-area-chart | Data | 2 |
| rx-alert | Feedback | 1 |
'use client';import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow,} from '@gremorie/rx-display';const REGISTRY_ITEMS = [ { id: 'rx-message', category: 'AI', deps: 4 }, { id: 'rx-button', category: 'Forms', deps: 1 }, { id: 'rx-card', category: 'Display', deps: 0 }, { id: 'rx-area-chart', category: 'Data', deps: 2 }, { id: 'rx-alert', category: 'Feedback', deps: 1 },];export function TablePreview() { return ( <Table> <TableCaption>A sample of registry items.</TableCaption> <TableHeader> <TableRow> <TableHead>Item</TableHead> <TableHead>Category</TableHead> <TableHead className="text-right">Deps</TableHead> </TableRow> </TableHeader> <TableBody> {REGISTRY_ITEMS.map((item) => ( <TableRow key={item.id}> <TableCell className="font-mono">{item.id}</TableCell> <TableCell>{item.category}</TableCell> <TableCell className="text-right">{item.deps}</TableCell> </TableRow> ))} </TableBody> </Table> );}Anatomia
Table wraps <table> in an overflow container
├─ TableCaption <caption>, rendered below the table
├─ TableHeader <thead>
│ └─ TableRow <tr> with hover + data-[state=selected]
│ └─ TableHead <th> column header
├─ TableBody <tbody>
│ └─ TableRow <tr> with hover + data-[state=selected]
│ └─ TableCell <td> body cell
└─ TableFooter <tfoot> (muted, top border)
└─ TableRow <tr> with hover + data-[state=selected]
└─ TableCell <td> summary cellInstalação
bash npx gremorie@latest add rx-table bash pnpm dlx gremorie@latest add rx-table bash yarn dlx gremorie@latest add rx-table bash bunx --bun gremorie@latest add rx-table Uso
import {
Table,
TableHeader,
TableBody,
TableRow,
TableHead,
TableCell,
} from "@gremorie/rx-display";
export function Example() {
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>Item</TableHead>
<TableHead>Category</TableHead>
<TableHead className="text-right">Deps</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-mono">rx-card</TableCell>
<TableCell>Display</TableCell>
<TableCell className="text-right">0</TableCell>
</TableRow>
</TableBody>
</Table>
);
}A edição Angular deste componente hoje é distribuída a partir do source (veja o side-by-side no workbench); sua entrada no registry vem a seguir.
API
<Table>
Envolve a <table> nativa em um container div.relative.w-full.overflow-x-auto para scroll horizontal em viewports estreitas. A própria table é w-full caption-bottom text-sm.
Todas as props de <table> são encaminhadas.
<TableHeader>
Renderiza como <thead>. Adiciona [&_tr]:border-b para que a linha embaixo do header sempre mostre o divisor.
<TableBody>
Renderiza como <tbody>. Remove a borda final na última linha via [&_tr:last-child]:border-0.
<TableFooter>
Renderiza como <tfoot>. Estilizado com bg-muted/50 border-t font-medium para linhas de soma ou linhas de resumo.
<TableRow>
Renderiza como <tr> com hover:bg-muted/50 (sensação interativa sem reivindicar interatividade), data-[state=selected]:bg-muted (funciona com o estado de seleção do TanStack Table) e border-b transition-colors.
<TableHead>
Renderiza como <th>. Padrões: h-10 px-2 text-left align-middle font-medium whitespace-nowrap. Combina com [role=checkbox] para colunas de seleção.
<TableCell>
Renderiza como <td>. Padrões: p-2 align-middle whitespace-nowrap. As mesmas acomodações de checkbox que TableHead.
<TableCaption>
Renderiza como <caption>. Padrões: text-muted-foreground mt-4 text-sm. A table pai é caption-bottom, então a caption aparece abaixo da table por padrão.
Composição
<Table>envolve tudo. Sempre inclua isto - o container de overflow é o que impede viewports estreitas de quebrar.<TableCaption>(opcional) descreve o conteúdo da table. Coloque como o primeiro child deTable- regra nativa do HTML, mesmo que renderize visualmente na base.<TableHeader>contém uma<TableRow>de células<TableHead>.<TableBody>contém as linhas de dados.<TableFooter>(opcional) contém totais, somas ou linhas de resumo.
Toda célula deve ser um TableCell ou TableHead - misturar <td> ou <th> crus funciona mas pula o tratamento consistente de padding e borda.
Variações
Data table com caption
| Item | Category | Deps |
|---|---|---|
| rx-message | AI | 4 |
| rx-card | Display | 0 |
| rx-button | Forms | 1 |
'use client';import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow,} from '@gremorie/rx-display';const REGISTRY_ITEMS = [ { id: 'rx-message', category: 'AI', deps: 4 }, { id: 'rx-card', category: 'Display', deps: 0 }, { id: 'rx-button', category: 'Forms', deps: 1 },];export function TableCaptionPreview() { return ( <Table> <TableCaption>Registry inventory by category.</TableCaption> <TableHeader> <TableRow> <TableHead>Item</TableHead> <TableHead>Category</TableHead> <TableHead className="text-right">Deps</TableHead> </TableRow> </TableHeader> <TableBody> {REGISTRY_ITEMS.map((item) => ( <TableRow key={item.id}> <TableCell className="font-mono">{item.id}</TableCell> <TableCell>{item.category}</TableCell> <TableCell className="text-right">{item.deps}</TableCell> </TableRow> ))} </TableBody> </Table> );}Coloque TableCaption como o primeiro child de Table (regra nativa do HTML). A table é caption-bottom, então ela renderiza abaixo das linhas.
Com resumo no footer
| Invoice | Status | Amount |
|---|---|---|
| INV-001 | Paid | $250.00 |
| INV-002 | Pending | $180.00 |
| INV-003 | Paid | $420.00 |
| Total | $850.00 | |
'use client';import { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow,} from '@gremorie/rx-display';const INVOICES = [ { id: 'INV-001', status: 'Paid', amount: '$250.00' }, { id: 'INV-002', status: 'Pending', amount: '$180.00' }, { id: 'INV-003', status: 'Paid', amount: '$420.00' },];export function TableWithFooterPreview() { return ( <Table> <TableHeader> <TableRow> <TableHead>Invoice</TableHead> <TableHead>Status</TableHead> <TableHead className="text-right">Amount</TableHead> </TableRow> </TableHeader> <TableBody> {INVOICES.map((invoice) => ( <TableRow key={invoice.id}> <TableCell>{invoice.id}</TableCell> <TableCell>{invoice.status}</TableCell> <TableCell className="text-right">{invoice.amount}</TableCell> </TableRow> ))} </TableBody> <TableFooter> <TableRow> <TableCell colSpan={2}>Total</TableCell> <TableCell className="text-right">$850.00</TableCell> </TableRow> </TableFooter> </Table> );}TableFooter renderiza como <tfoot> com bg-muted/50 border-t font-medium - ideal para totais ou linhas de resumo. Use colSpan para abranger colunas de label.
Estado de linha selecionada
<TableRow data-state={selected ? 'selected' : undefined}>
<TableCell>...</TableCell>
</TableRow>O estilo data-[state=selected]:bg-muted é captado automaticamente. O TanStack Table define este atributo de graça quando você usa seu modelo de seleção de linha.
Acessibilidade
- Semântica nativa: cada parte renderiza como um elemento HTML real (
table,thead,tbody,th,td,caption), então leitores de tela anunciam as relações de linha e coluna corretamente. Nenhum ARIA necessário para tables estáticas. - Captions: use
TableCaptionpara resumir o conteúdo da table em uma linha ("Registry inventory by category"). Coloque como o primeiro child deTable- requisito nativo do HTML. - Escopo de coluna: para tables com headers de linha, adicione
scope="row"na célula mais à esquerda escope="col"nas células de header. Ambos são atributos nativos que você passa através deTableHead/TableCell. - Colunas de seleção: quando uma linha tem um checkbox, os estilos inline
[&:has([role=checkbox])]:pr-0e[&>[role=checkbox]]:translate-y-[2px]alinham o checkbox corretamente. Use umrole="checkbox"real ou um primitiveCheckbox. - Colunas ordenáveis: a ordenação ARIA (
aria-sort="ascending" \| "descending" \| "none") pertence aoTableHead. Conecte-a na camada de pattern DataTable. - Headers sticky: o CSS nativo
position: stickyfunciona em<th>dentro de<thead>. AdicioneclassName="sticky top-0 bg-background"para headers travados no scroll. - Scroll de overflow: o wrapper
div.overflow-x-autopermite scroll horizontal em viewports estreitas - adicionetabIndex={0}erole="region"no wrapper se você precisa de scroll por teclado.
Relacionados
- Card - host comum para uma Table dentro de um tile de dashboard.
- Pagination - combina com Table para views paginadas.
- Checkbox - coloque em colunas de seleção.
- Padrão DataTable (camada de Patterns) - compõe Table com TanStack Table para ordenação, filtragem, paginação e seleção.