Saltar al contenido principal

Table

Organizar datos densos. Filas limpias. Facilitar análisis.

Billing history for the Acme workspace.
InvoiceBilling periodStatusAmount
INV-2481Mar 2025Paid$248.00
INV-2480Feb 2025Paid$248.00
INV-2479Jan 2025Pending$248.00
INV-2478Dec 2024Overdue$186.00
Billed this period$930.00
1import { Badge } from '@/components/ui/badge';
2import { Table } from '@/components/ui/table';
3
4const invoices = [
5 {
6 id: 'INV-2481',
7 period: 'Mar 2025',
8 status: 'Paid',
9 tone: 'success',
10 amount: '$248.00',
11 },
12 {
13 id: 'INV-2480',
14 period: 'Feb 2025',
15 status: 'Paid',
16 tone: 'success',
17 amount: '$248.00',
18 },
19 {
20 id: 'INV-2479',
21 period: 'Jan 2025',
22 status: 'Pending',
23 tone: 'warning',
24 amount: '$248.00',
25 },
26 {
27 id: 'INV-2478',
28 period: 'Dec 2024',
29 status: 'Overdue',
30 tone: 'destructive',
31 amount: '$186.00',
32 },
33] as const;
34
35export function Default() {
36 return (
37 <Table>
38 <Table.Caption>Billing history for the Acme workspace.</Table.Caption>
39 <Table.Header>
40 <Table.Row>
41 <Table.Head className="w-[110px]">Invoice</Table.Head>
42 <Table.Head>Billing period</Table.Head>
43 <Table.Head>Status</Table.Head>
44 <Table.Head className="text-right">Amount</Table.Head>
45 </Table.Row>
46 </Table.Header>
47 <Table.Body>
48 {invoices.map((invoice) => (
49 <Table.Row key={invoice.id}>
50 <Table.Cell className="font-medium">{invoice.id}</Table.Cell>
51 <Table.Cell>{invoice.period}</Table.Cell>
52 <Table.Cell>
53 <Badge variant={invoice.tone}>{invoice.status}</Badge>
54 </Table.Cell>
55 <Table.Cell className="text-right tabular-nums">{invoice.amount}</Table.Cell>
56 </Table.Row>
57 ))}
58 </Table.Body>
59 <Table.Footer>
60 <Table.Row>
61 <Table.Cell colSpan={3}>Billed this period</Table.Cell>
62 <Table.Cell className="text-right tabular-nums">$930.00</Table.Cell>
63 </Table.Row>
64 </Table.Footer>
65 </Table>
66 );
67}

Instalación

pnpm dlx nachui add table

Anatomía

1import { Table } from '@/components/ui/table';
1<Table>
2 <Table.Caption>A list of your recent invoices.</Table.Caption>
3 <Table.Header>
4 <Table.Row>
5 <Table.Head>Head</Table.Head>
6 </Table.Row>
7 </Table.Header>
8 <Table.Body>
9 <Table.Row>
10 <Table.Cell>Cell</Table.Cell>
11 </Table.Row>
12 </Table.Body>
13 <Table.Footer>
14 <Table.Row>
15 <Table.Cell>Footer</Table.Cell>
16 </Table.Row>
17 </Table.Footer>
18</Table>

Composición

Usa la siguiente composición para construir un Table:
Table
├── Table.Caption
├── Table.Header
│ └── Table.Row
│ └── Table.Head
├── Table.Body
│ └── Table.Row
│ └── Table.Cell
└── Table.Footer
└── Table.Row
└── Table.Cell

Variantes

Con acciones (With Actions)

API keys for the storefront-api project.
NameKeyStatusActions
Productionsk_live_••••4f2aActiveUsed 2m ago
CI pipelinesk_live_••••9b71ActiveUsed 3h ago
Local devsk_test_••••1c05ActiveUsed Mar 12
1'use client';
2
3import { useState } from 'react';
4
5import { Badge } from '@/components/ui/badge';
6import { Button } from '@/components/ui/button';
7import { Table } from '@/components/ui/table';
8
9const apiKeys = [
10 { id: 'key_prod', name: 'Production', secret: 'sk_live_••••4f2a', lastUsed: '2m ago' },
11 { id: 'key_ci', name: 'CI pipeline', secret: 'sk_live_••••9b71', lastUsed: '3h ago' },
12 { id: 'key_dev', name: 'Local dev', secret: 'sk_test_••••1c05', lastUsed: 'Mar 12' },
13];
14
15export function WithActions() {
16 const [revoked, setRevoked] = useState<string[]>([]);
17
18 return (
19 <Table>
20 <Table.Caption>API keys for the storefront-api project.</Table.Caption>
21 <Table.Header>
22 <Table.Row>
23 <Table.Head>Name</Table.Head>
24 <Table.Head>Key</Table.Head>
25 <Table.Head>Status</Table.Head>
26 <Table.Head className="text-right">Actions</Table.Head>
27 </Table.Row>
28 </Table.Header>
29 <Table.Body>
30 {apiKeys.map((apiKey) => {
31 const isRevoked = revoked.includes(apiKey.id);
32
33 return (
34 <Table.Row key={apiKey.id}>
35 <Table.Cell className="font-medium">{apiKey.name}</Table.Cell>
36 <Table.Cell className="font-mono">{apiKey.secret}</Table.Cell>
37 <Table.Cell>
38 {isRevoked ? (
39 <Badge variant="secondary">Revoked</Badge>
40 ) : (
41 <span className="flex items-center gap-2">
42 <Badge variant="success">Active</Badge>
43 <span className="text-muted-foreground">Used {apiKey.lastUsed}</span>
44 </span>
45 )}
46 </Table.Cell>
47 <Table.Cell className="text-right">
48 <div className="flex justify-end gap-2">
49 <Button variant="ghost" className="h-8 px-2 text-xs" disabled={isRevoked}>
50 Copy
51 </Button>
52 <Button
53 variant="ghost"
54 className="text-destructive-text h-8 px-2 text-xs"
55 disabled={isRevoked}
56 onClick={() => setRevoked((keys) => [...keys, apiKey.id])}
57 >
58 Revoke
59 </Button>
60 </div>
61 </Table.Cell>
62 </Table.Row>
63 );
64 })}
65 </Table.Body>
66 </Table>
67 );
68}

Rayado (Striped)

Traffic by endpoint over the last 24 hours.
EndpointRequestsError rateStatus
GET /v1/orders128,4020.02%Healthy
POST /v1/orders41,9080.11%Healthy
GET /v1/customers96,1200.04%Healthy
POST /v1/webhooks/stripe8,4412.40%Degraded
DELETE /v1/sessions3,2045.80%Failing
1import { Badge } from '@/components/ui/badge';
2import { Table } from '@/components/ui/table';
3import { cn } from '@/lib/cn';
4
5const endpoints = [
6 {
7 route: 'GET /v1/orders',
8 requests: '128,402',
9 errorRate: '0.02%',
10 status: 'Healthy',
11 tone: 'success',
12 },
13 {
14 route: 'POST /v1/orders',
15 requests: '41,908',
16 errorRate: '0.11%',
17 status: 'Healthy',
18 tone: 'success',
19 },
20 {
21 route: 'GET /v1/customers',
22 requests: '96,120',
23 errorRate: '0.04%',
24 status: 'Healthy',
25 tone: 'success',
26 },
27 {
28 route: 'POST /v1/webhooks/stripe',
29 requests: '8,441',
30 errorRate: '2.40%',
31 status: 'Degraded',
32 tone: 'warning',
33 },
34 {
35 route: 'DELETE /v1/sessions',
36 requests: '3,204',
37 errorRate: '5.80%',
38 status: 'Failing',
39 tone: 'destructive',
40 },
41] as const;
42
43export function Striped() {
44 return (
45 <Table>
46 <Table.Caption>Traffic by endpoint over the last 24 hours.</Table.Caption>
47 <Table.Header>
48 <Table.Row>
49 <Table.Head>Endpoint</Table.Head>
50 <Table.Head className="text-right">Requests</Table.Head>
51 <Table.Head className="text-right">Error rate</Table.Head>
52 <Table.Head>Status</Table.Head>
53 </Table.Row>
54 </Table.Header>
55 <Table.Body>
56 {endpoints.map((endpoint, index) => (
57 <Table.Row key={endpoint.route} className={cn(index % 2 === 0 && 'bg-muted/30')}>
58 <Table.Cell className="font-mono font-medium">{endpoint.route}</Table.Cell>
59 <Table.Cell className="text-right tabular-nums">{endpoint.requests}</Table.Cell>
60 <Table.Cell className="text-right tabular-nums">{endpoint.errorRate}</Table.Cell>
61 <Table.Cell>
62 <Badge variant={endpoint.tone}>{endpoint.status}</Badge>
63 </Table.Cell>
64 </Table.Row>
65 ))}
66 </Table.Body>
67 </Table>
68 );
69}

Compacto (Compact)

DeploymentBranchStatusDuration
dpl_9fk2axmainReady42s
dpl_7ta1mvfeat/checkout-v2Building1m 12s
dpl_5qr8bdfix/webhook-retryFailed18s
dpl_3nz6ykmainReady39s
dpl_2hb4lpchore/bump-depsCanceled6s
1import { Badge } from '@/components/ui/badge';
2import { Table } from '@/components/ui/table';
3
4const deployments = [
5 {
6 id: 'dpl_9fk2ax',
7 branch: 'main',
8 status: 'Ready',
9 tone: 'success',
10 duration: '42s',
11 },
12 {
13 id: 'dpl_7ta1mv',
14 branch: 'feat/checkout-v2',
15 status: 'Building',
16 tone: 'info',
17 duration: '1m 12s',
18 },
19 {
20 id: 'dpl_5qr8bd',
21 branch: 'fix/webhook-retry',
22 status: 'Failed',
23 tone: 'destructive',
24 duration: '18s',
25 },
26 {
27 id: 'dpl_3nz6yk',
28 branch: 'main',
29 status: 'Ready',
30 tone: 'success',
31 duration: '39s',
32 },
33 {
34 id: 'dpl_2hb4lp',
35 branch: 'chore/bump-deps',
36 status: 'Canceled',
37 tone: 'secondary',
38 duration: '6s',
39 },
40] as const;
41
42export function Compact() {
43 return (
44 <Table>
45 <Table.Header>
46 <Table.Row>
47 <Table.Head className="h-auto py-2 text-xs">Deployment</Table.Head>
48 <Table.Head className="h-auto py-2 text-xs">Branch</Table.Head>
49 <Table.Head className="h-auto py-2 text-xs">Status</Table.Head>
50 <Table.Head className="h-auto py-2 text-right text-xs">Duration</Table.Head>
51 </Table.Row>
52 </Table.Header>
53 <Table.Body>
54 {deployments.map((deployment) => (
55 <Table.Row key={deployment.id}>
56 <Table.Cell className="py-2 font-mono font-medium">{deployment.id}</Table.Cell>
57 <Table.Cell className="text-muted-foreground py-2">{deployment.branch}</Table.Cell>
58 <Table.Cell className="py-2">
59 <Badge variant={deployment.tone}>{deployment.status}</Badge>
60 </Table.Cell>
61 <Table.Cell className="py-2 text-right tabular-nums">{deployment.duration}</Table.Cell>
62 </Table.Row>
63 ))}
64 </Table.Body>
65 </Table>
66 );
67}

Referencia de API

Table

PropTipoDefaultDescripción
classNamestringClases adicionales.

Table.Header

PropTipoDefaultDescripción
classNamestringClases adicionales.

Table.Body

PropTipoDefaultDescripción
classNamestringClases adicionales.

Table.Footer

PropTipoDefaultDescripción
classNamestringClases adicionales.

Table.Row

PropTipoDefaultDescripción
classNamestringClases adicionales.

Table.Head

PropTipoDefaultDescripción
classNamestringClases adicionales.

Table.Cell

PropTipoDefaultDescripción
classNamestringClases adicionales.

Table.Caption

PropTipoDefaultDescripción
classNamestringClases adicionales.
¿Encontraste algo que mejorar?

¿Notaste un error, tipografía o detalle faltante en esta página? Ayúdanos a mejorar la documentación abriendo un issue en GitHub.

Crear un Issue
Ctrl+I