Tabla de datos
Tablas ordenables, filtrables y paginadas para paneles de administración, cuadros de mando y listas de pedidos, con ese estilo neobrutalista de bordes y sombras.
La tabla de datos es una guía, no un componente empaquetado: compones tu propia tabla con el componente <Table /> y TanStack Table, un motor headless de ordenación, filtrado, paginación y selección de filas, mientras la receta neobrutalista de bordes gruesos, sombras duras y tipografía contundente se ocupa del aspecto.
Úsalo cuando:
- Paneles de administración — pedidos, usuarios y facturas con columnas ordenables, filtros y menús de acciones por fila.
- Dashboards — tablas de pagos o de actividad con celdas formateadas, columnas de estado y datos paginados de la API.
- Flujos masivos — selección de filas con casillas para exportar, archivar o eliminar muchos registros de una sola vez.
Introducción#
Cada tabla de datos o cuadrícula de datos que he creado ha sido única. Todas se comportan de forma distinta, tienen requisitos específicos de ordenación y filtrado, y funcionan con fuentes de datos diferentes.
No tiene sentido combinar todas estas variaciones en un único componente. Si lo hiciéramos, perderíamos la flexibilidad que ofrece la UI headless.
Así que, en lugar de un componente data-table, me pareció más útil ofrecer una guía para construir el tuyo propio.
Empezaremos con el componente básico <Table /> y construiremos una tabla de datos compleja desde cero.
Consejo: Si acabas usando la misma tabla en varios lugares de tu aplicación, siempre puedes extraerla a un componente reutilizable.
Índice#
Esta guía te muestra cómo usar TanStack Table y el componente <Table /> para construir tu propia tabla de datos personalizada. Cubriremos los siguientes temas:
- Tabla básica
- Acciones de fila
- Paginación
- Ordenación
- Filtrado
- Visibilidad
- Selección de filas
- Componentes reutilizables
Instalación#
- Añade el componente
<Table />a tu proyecto:
pnpm dlx shadcn@latest add tablenpx shadcn@latest add tableyarn dlx shadcn@latest add tablebunx --bun shadcn@latest add table
- Añade la dependencia
tanstack/react-table:
pnpm add @tanstack/react-tablenpm install @tanstack/react-tableyarn add @tanstack/react-tablebun add @tanstack/react-table
Requisitos previos#
Vamos a construir una tabla para mostrar los pagos recientes. Así son nuestros datos:
type Payment = {
id: string
amount: number
status: "pending" | "processing" | "success" | "failed"
email: string
}
export const payments: Payment[] = [
{
id: "728ed52f",
amount: 100,
status: "pending",
email: "[email protected]",
},
{
id: "489e1d42",
amount: 125,
status: "processing",
email: "[email protected]",
},
// ...
]Estructura del proyecto#
Empieza creando la siguiente estructura de archivos:
app
└── payments
├── columns.tsx
├── data-table.tsx
└── page.tsxAquí utilizo un ejemplo con Next.js, pero esto funciona con cualquier otro framework de React.
columns.tsx(componente cliente) contendrá nuestras definiciones de columnas.data-table.tsx(componente cliente) contendrá nuestro componente<DataTable />.page.tsx(componente servidor) es donde obtendremos los datos y renderizaremos nuestra tabla.
Tabla básica#
Empecemos construyendo una tabla básica.
Definiciones de columnas#
Primero, definiremos nuestras columnas.
"use client"
import { ColumnDef } from "@tanstack/react-table"
// This type is used to define the shape of our data.
// You can use a Zod schema here if you want.
export type Payment = {
id: string
amount: number
status: "pending" | "processing" | "success" | "failed"
email: string
}
export const columns: ColumnDef<Payment>[] = [
{
accessorKey: "status",
header: "Status",
},
{
accessorKey: "email",
header: "Email",
},
{
accessorKey: "amount",
header: "Amount",
},
]Nota: En las columnas defines el núcleo del aspecto de tu tabla. Determinan los datos que se mostrarán, así como su formato, ordenación y filtrado.
Componente <DataTable />#
A continuación, crearemos un componente <DataTable /> para renderizar nuestra tabla.
"use client"
import {
ColumnDef,
flexRender,
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
}
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
})
return (
<div className="overflow-hidden rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
)
}Consejo: Si acabas usando <DataTable /> en varios lugares, este es el componente que podrías hacer reutilizable extrayéndolo a components/ui/data-table.tsx.
<DataTable columns={columns} data={data} />
Renderizar la tabla#
Por último, renderizaremos nuestra tabla en nuestro componente de página.
import { columns, Payment } from "./columns"
import { DataTable } from "./data-table"
async function getData(): Promise<Payment[]> {
// Fetch data from your API here.
return [
{
id: "728ed52f",
amount: 100,
status: "pending",
email: "[email protected]",
},
// ...
]
}
export default async function DemoPage() {
const data = await getData()
return (
<div className="container mx-auto py-10">
<DataTable columns={columns} data={data} />
</div>
)
}Formato de celdas#
Vamos a dar formato a la celda del importe para mostrar la cantidad en dólares. También alinearemos la celda a la derecha.
Actualizar la definición de columnas#
Actualiza las definiciones header y cell del importe de la siguiente manera:
export const columns: ColumnDef<Payment>[] = [
{
accessorKey: "amount",
header: () => <div className="text-right">Amount</div>,
cell: ({ row }) => {
const amount = parseFloat(row.getValue("amount"))
const formatted = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
}).format(amount)
return <div className="text-right font-medium">{formatted}</div>
},
},
]Puedes usar el mismo enfoque para dar formato a otras celdas y encabezados.
Acciones de fila#
Vamos a añadir acciones de fila a nuestra tabla. Para ello usaremos un componente <DropdownMenu />.
Actualizar la definición de columnas#
Actualiza la definición de columnas para añadir una nueva columna actions. La celda actions devuelve un componente <DropdownMenu />.
"use client"
import { ColumnDef } from "@tanstack/react-table"
import { MoreHorizontal } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
export const columns: ColumnDef<Payment>[] = [
// ...
{
id: "actions",
cell: ({ row }) => {
const payment = row.original
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(payment.id)}
>
Copy payment ID
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>View customer</DropdownMenuItem>
<DropdownMenuItem>View payment details</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
},
},
// ...
]Puedes acceder a los datos de la fila mediante row.original en la función cell. Úsalo para gestionar las acciones de tu fila; por ejemplo, usa el id para hacer una petición DELETE a tu API.
Paginación#
A continuación, añadiremos paginación a nuestra tabla.
Actualizar <DataTable>#
import {
ColumnDef,
flexRender,
getCoreRowModel,
getPaginationRowModel,
useReactTable,
} from "@tanstack/react-table"
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
})
// ...
}Esto paginará automáticamente tus filas en páginas de 10. Consulta la documentación de paginación para obtener más información sobre cómo personalizar el tamaño de página e implementar la paginación manual.
Añadir controles de paginación#
Podemos añadir controles de paginación a nuestra tabla mediante el componente <Button /> y los métodos de API table.previousPage() y table.nextPage().
import { Button } from "@/components/ui/button"
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
})
return (
<div>
<div className="overflow-hidden rounded-md border">
<Table>
{ // .... }
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
)
}Consulta la sección Componentes reutilizables para ver un componente de paginación más avanzado.
Ordenación#
Vamos a hacer que la columna de correo electrónico sea ordenable.
Actualizar <DataTable>#
"use client"
import * as React from "react"
import {
ColumnDef,
SortingState,
flexRender,
getCoreRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table"
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = React.useState<SortingState>([])
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
state: {
sorting,
},
})
return (
<div>
<div className="overflow-hidden rounded-md border">
<Table>{ ... }</Table>
</div>
</div>
)
}Hacer que la celda de encabezado sea ordenable#
Ahora podemos actualizar la celda de encabezado email para añadir controles de ordenación.
"use client"
import { ColumnDef } from "@tanstack/react-table"
import { ArrowUpDown } from "lucide-react"
export const columns: ColumnDef<Payment>[] = [
{
accessorKey: "email",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Email
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
},
]Esto ordenará automáticamente la tabla (ascendente y descendente) cuando el usuario haga clic en la celda de encabezado.
Filtrado#
Vamos a añadir un campo de búsqueda para filtrar los correos electrónicos de nuestra tabla.
Actualizar <DataTable>#
"use client"
import * as React from "react"
import {
ColumnDef,
ColumnFiltersState,
SortingState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
)
const table = useReactTable({
data,
columns,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
onColumnFiltersChange: setColumnFilters,
getFilteredRowModel: getFilteredRowModel(),
state: {
sorting,
columnFilters,
},
})
return (
<div>
<div className="flex items-center py-4">
<Input
placeholder="Filter emails..."
value={(table.getColumn("email")?.getFilterValue() as string) ?? ""}
onChange={(event) =>
table.getColumn("email")?.setFilterValue(event.target.value)
}
className="max-w-sm"
/>
</div>
<div className="overflow-hidden rounded-md border">
<Table>{ ... }</Table>
</div>
</div>
)
}El filtrado ya está activado para la columna email. También puedes añadir filtros a otras columnas. Consulta la documentación de filtrado para obtener más información sobre cómo personalizar los filtros.
Visibilidad#
Añadir la visibilidad de columnas es bastante sencillo con la API de visibilidad de @tanstack/react-table.
Actualizar <DataTable>#
"use client"
import * as React from "react"
import {
ColumnDef,
ColumnFiltersState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
)
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({})
const table = useReactTable({
data,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
state: {
sorting,
columnFilters,
columnVisibility,
},
})
return (
<div>
<div className="flex items-center py-4">
<Input
placeholder="Filter emails..."
value={table.getColumn("email")?.getFilterValue() as string}
onChange={(event) =>
table.getColumn("email")?.setFilterValue(event.target.value)
}
className="max-w-sm"
/>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="ml-auto">
Columns
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{table
.getAllColumns()
.filter(
(column) => column.getCanHide()
)
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) =>
column.toggleVisibility(!!value)
}
>
{column.id}
</DropdownMenuCheckboxItem>
)
})}
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="overflow-hidden rounded-md border">
<Table>{ ... }</Table>
</div>
</div>
)
}Esto añade un menú desplegable que puedes usar para mostrar u ocultar las columnas.
Selección de filas#
A continuación, vamos a añadir la selección de filas a nuestra tabla.
Actualizar las definiciones de columnas#
"use client"
import { ColumnDef } from "@tanstack/react-table"
import { Badge } from "@/components/ui/badge"
import { Checkbox } from "@/components/ui/checkbox"
export const columns: ColumnDef<Payment>[] = [
{
id: "select",
header: ({ table }) => (
<Checkbox
checked={
table.getIsAllPageRowsSelected() ||
(table.getIsSomePageRowsSelected() && "indeterminate")
}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
aria-label="Select all"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
/>
),
enableSorting: false,
enableHiding: false,
},
]Actualizar <DataTable>#
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
)
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({})
const [rowSelection, setRowSelection] = React.useState({})
const table = useReactTable({
data,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
},
})
return (
<div>
<div className="overflow-hidden rounded-md border">
<Table />
</div>
</div>
)
}Esto añade una casilla a cada fila y una casilla en el encabezado para seleccionar todas las filas.
Mostrar las filas seleccionadas#
Puedes mostrar el número de filas seleccionadas mediante la API table.getFilteredSelectedRowModel().
<div className="flex-1 text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>Componentes reutilizables#
Aquí tienes algunos componentes que puedes usar para construir tus tablas de datos. Provienen de la demo Tasks.
Encabezado de columna#
Haz que cualquier encabezado de columna sea ordenable y ocultable.
import { type Column } from "@tanstack/react-table"
import { ArrowDown, ArrowUp, ChevronsUpDown, EyeOff } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/registry/base/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/registry/base/ui/dropdown-menu"
interface DataTableColumnHeaderProps<
TData,
TValue,
> extends React.HTMLAttributes<HTMLDivElement> {
column: Column<TData, TValue>
title: string
}
export function DataTableColumnHeader<TData, TValue>({
column,
title,
className,
}: DataTableColumnHeaderProps<TData, TValue>) {
if (!column.getCanSort()) {
return <div className={cn(className)}>{title}</div>
}
return (
<div className={cn("flex items-center gap-2", className)}>
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button
variant="ghost"
size="sm"
className="-ml-3 h-8 data-popup-open:bg-accent"
/>
}
>
<span>{title}</span>
{column.getIsSorted() === "desc" ? (
<ArrowDown />
) : column.getIsSorted() === "asc" ? (
<ArrowUp />
) : (
<ChevronsUpDown />
)}
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem onClick={() => column.toggleSorting(false)}>
<ArrowUp />
Asc
</DropdownMenuItem>
<DropdownMenuItem onClick={() => column.toggleSorting(true)}>
<ArrowDown />
Desc
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
<EyeOff />
Hide
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
)
}
export const columns = [
{
accessorKey: "email",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Email" />
),
},
]Paginación#
Añade controles de paginación a tu tabla, incluidos el tamaño de página y el número de elementos seleccionados.
import { type Table } from "@tanstack/react-table"
import {
ChevronLeft,
ChevronRight,
ChevronsLeft,
ChevronsRight,
} from "lucide-react"
import { Button } from "@/registry/base/ui/button"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/registry/base/ui/select"
const pageSizeItems = [10, 20, 25, 30, 40, 50].map((pageSize) => ({
label: `${pageSize}`,
value: `${pageSize}`,
}))
interface DataTablePaginationProps<TData> {
table: Table<TData>
}
export function DataTablePagination<TData>({
table,
}: DataTablePaginationProps<TData>) {
return (
<div className="flex items-center justify-between px-2">
<div className="flex-1 text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
<div className="flex items-center space-x-6 lg:space-x-8">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium">Rows per page</p>
<Select
items={pageSizeItems}
value={`${table.getState().pagination.pageSize}`}
onValueChange={(value) => {
if (value !== null) {
table.setPageSize(Number(value))
}
}}
>
<SelectTrigger className="h-8 w-[70px]">
<SelectValue />
</SelectTrigger>
<SelectContent side="top">
{pageSizeItems.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
Page {table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
</div>
<div className="flex items-center space-x-2">
<Button
variant="outline"
size="icon"
className="hidden size-8 lg:flex"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to first page</span>
<ChevronsLeft />
</Button>
<Button
variant="outline"
size="icon"
className="size-8"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to previous page</span>
<ChevronLeft />
</Button>
<Button
variant="outline"
size="icon"
className="size-8"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to next page</span>
<ChevronRight />
</Button>
<Button
variant="outline"
size="icon"
className="hidden size-8 lg:flex"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to last page</span>
<ChevronsRight />
</Button>
</div>
</div>
</div>
)
}
<DataTablePagination table={table} />Alternar columnas#
Un componente para mostrar u ocultar las columnas.
"use client"
import { type Table } from "@tanstack/react-table"
import { Settings2 } from "lucide-react"
import { Button } from "@/registry/base/ui/button"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/registry/base/ui/dropdown-menu"
export function DataTableViewOptions<TData>({
table,
}: {
table: Table<TData>
}) {
return (
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button
variant="outline"
size="sm"
className="ml-auto hidden h-8 lg:flex"
/>
}
>
<Settings2 />
View
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[150px]">
{/* GroupLabel (Base UI) needs a Group ancestor or it throws error #31. */}
<DropdownMenuGroup>
<DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
</DropdownMenuGroup>
<DropdownMenuSeparator />
{table
.getAllColumns()
.filter(
(column) =>
typeof column.accessorFn !== "undefined" && column.getCanHide()
)
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) => column.toggleVisibility(!!value)}
>
{column.id}
</DropdownMenuCheckboxItem>
)
})}
</DropdownMenuContent>
</DropdownMenu>
)
}
<DataTableViewOptions table={table} />RTL#
Para activar el soporte RTL en Neobrutalism, consulta la guía de configuración de RTL.
Accesibilidad#
TanStack Table es headless: no renderiza ningún marcado, así que la accesibilidad proviene de los elementos de esta guía. El componente <Table /> genera elementos nativos <table>, <th> y <td>, que dan a los lectores de pantalla el contexto de fila y columna conforme al patrón Table de WAI-ARIA. Dos cosas que mantener mientras construyes: establece aria-sort en la cabecera ordenada en cada momento, y conserva el aria-label de las casillas de selección y el texto sr-only de los disparadores de solo icono (los fragmentos de arriba hacen ambas cosas).
Todos los controles de esta guía son botones, casillas o campos nativos, así que el comportamiento del teclado es el predeterminado del navegador:
| Tecla | Acción |
|---|---|
Tab / Shift + Tab | Recorre los controles de la tabla: campo de filtro, cabeceras de ordenación, menús de fila, paginación |
Space / Enter | Activa el botón de ordenación o de paginación enfocado |
Space | Alterna la casilla de la fila enfocada o la de seleccionar todo |