- Docs
- components
- Table
Table
A table displays data in rows and columns and enables a user to navigate its contents via directional navigation keys, and optionally supports row selection and sorting.
Preview
Code
Invoice | Status | Method | Amount |
---|---|---|---|
INV001 | Paid | Credit Card | $250.00 |
INV002 | Pending | PayPal | $150.00 |
INV003 | Unpaid | Bank Transfer | $350.00 |
INV004 | Paid | Credit Card | $450.00 |
INV005 | Paid | PayPal | $550.00 |
INV006 | Pending | Bank Transfer | $200.00 |
INV007 | Unpaid | Credit Card | $300.00 |
Installation
CLI
Manual
Copy and paste the following code into your project.
"use client";
import {
Cell,
Collection,
Column,
ColumnResizer,
ResizableTableContainer,
TableBody as TableBodyPrimitive,
TableHeader as TableHeaderPrimitive,
Table as TablePrimitive,
Row as TableRowPrimitive,
useTableOptions,
} from "react-aria-components";
import type { VariantProps } from "~/lib/cva";
import { ButtonPrimitive } from "../button";
import { Checkbox } from "../checkbox";
import { TableStyles } from "./styles";
const Icons = {
ChevronDown: (props) => (
<svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
width={32}
height={32}
viewBox="0 0 24 24"
{...props}
>
<path
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 9l6 6 6-6"
/>
</svg>
),
} as const satisfies Record<
string,
(props: React.ComponentProps<"svg">) => React.JSX.Element
>;
export interface TableRootProps
extends React.ComponentProps<typeof TablePrimitive> {
allowResize?: boolean;
}
export function TableRoot({ children, className, ...props }: TableRootProps) {
return (
<div className="relative w-full">
{props.allowResize ? (
<ResizableTableContainer className="overflow-auto">
<TablePrimitive
{...props}
className={TableStyles.Root({ className })}
data-resizable="true"
>
{children}
</TablePrimitive>
</ResizableTableContainer>
) : (
<TablePrimitive
{...props}
data-resizable="false"
className={TableStyles.Root({ className })}
>
{children}
</TablePrimitive>
)}
</div>
);
}
export interface TableHeaderProps<T extends object>
extends React.ComponentProps<typeof TableHeaderPrimitive<T>> {}
export function TableHeader<T extends object>({
className,
columns,
children,
...props
}: TableHeaderProps<T>) {
const { selectionBehavior, selectionMode, allowsDragging } =
useTableOptions();
return (
<TableHeaderPrimitive
{...props}
className={TableStyles.Header({ className })}
>
{allowsDragging && <TableColumn className="w-0" />}
{selectionBehavior === "toggle" && (
<TableColumn className="w-0 pl-4">
{selectionMode === "multiple" && (
<Checkbox.Provider slot="selection">
<Checkbox.Root>
<Checkbox.Indicator />
</Checkbox.Root>
</Checkbox.Provider>
)}
</TableColumn>
)}
<Collection items={columns}>{children}</Collection>
</TableHeaderPrimitive>
);
}
export interface TableColumnProps
extends React.ComponentProps<typeof Column>,
VariantProps<(typeof TableStyles)["Column"]> {}
export function TableColumn({
className,
isResizable,
...props
}: TableColumnProps) {
return (
<Column
{...props}
className={(values) =>
TableStyles.Column({
isResizable,
className:
typeof className === "function" ? className(values) : className,
})
}
/>
);
}
export interface TableColumnResizerProps
extends React.ComponentProps<typeof ColumnResizer> {}
export function TableColumnResizer({
className,
...props
}: TableColumnResizerProps) {
return (
<ColumnResizer
{...props}
className={(values) =>
TableStyles.ColumnResizer({
className:
typeof className === "function" ? className(values) : className,
})
}
>
<div className="h-full w-px bg-border py-3" />
</ColumnResizer>
);
}
export interface TableBodyProps<T extends object>
extends React.ComponentProps<typeof TableBodyPrimitive<T>> {}
export function TableBody<T extends object>({
className,
...props
}: TableBodyProps<T>) {
return (
<TableBodyPrimitive
{...props}
className={(values) =>
TableStyles.Body({
className:
typeof className === "function" ? className(values) : className,
})
}
/>
);
}
export interface TableRowProps<T extends object>
extends React.ComponentProps<typeof TableRowPrimitive<T>> {}
export function TableRow<T extends object>({
className,
columns,
children,
...props
}: TableRowProps<T>) {
const { allowsDragging, selectionBehavior } = useTableOptions();
return (
<TableRowPrimitive
{...props}
className={(values) =>
TableStyles.Row({
className:
typeof className === "function" ? className(values) : className,
})
}
>
{allowsDragging && (
<Cell className="group cursor-grab dragging:cursor-grabbing pr-0 ring-primary">
<ButtonPrimitive
className="relative bg-transparent py-1.5 pl-3.5 pressed:text-fg text-muted-fg"
slot="drag"
>
<Icons.ChevronDown />
</ButtonPrimitive>
</Cell>
)}
{selectionBehavior === "toggle" && (
<Cell className="pl-4">
<span
aria-hidden
className="absolute inset-y-0 left-0 hidden h-full w-0.5 bg-primary group-selected:block"
/>
<Checkbox.Provider slot="selection">
<Checkbox.Root>
<Checkbox.Indicator />
</Checkbox.Root>
</Checkbox.Provider>
</Cell>
)}
<Collection items={columns}>{children}</Collection>
</TableRowPrimitive>
);
}
export interface TableCellProps extends React.ComponentProps<typeof Cell> {}
export function TableCell({ className, ...props }: TableCellProps) {
return (
<Cell
{...props}
className={(values) =>
TableStyles.Cell({
className:
typeof className === "function" ? className(values) : className,
})
}
/>
);
}
export interface TableSortIconProps extends React.ComponentProps<"svg"> {}
export function TableSortIcon({ className, ...props }: TableSortIconProps) {
return (
<Icons.ChevronDown
{...props}
className={TableStyles.SortIcon({
className,
})}
/>
);
}
// export interface TableFooterProps extends React.ComponentProps<"tfoot"> {}
// export function TableFooter({ className, ...props }: TableFooterProps) {
// return <tfoot {...props} className={TableStyles.Footer({ className })} />;
// }
// export interface TableCaptionProps extends React.ComponentProps<"caption"> {}
// export function TableCaption({ className, ...props }: TableCaptionProps) {
// return <caption {...props} className={TableStyles.Caption({ className })} />;
// }
Update the import paths to match your project setup.
Usage
Single import
import { Table } from "~/components/ui/table";
<Table.Root>
<Table.Caption>A list of your recent invoices.</Table.Caption>
<Table.Header>
<Table.Row>
<Table.Head className="w-[100px]">Invoice</Table.Head>
<Table.Head>Status</Table.Head>
<Table.Head>Method</Table.Head>
<Table.Head className="text-right">Amount</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell className="font-medium">INV001</Table.Cell>
<Table.Cell>Paid</Table.Cell>
<Table.Cell>Credit Card</Table.Cell>
<Table.Cell className="text-right">$250.00</Table.Cell>
</Table.Row>
</Table.Body>
<Table.Footer>
<Table.Row>
<Table.Cell colSpan={3}>Total</Table.Cell>
<Table.Cell className="text-right">$2,500.00</Table.Cell>
</Table.Row>
</Table.Footer>
</Table.Root>
Multiple imports
import {
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRoot,
TableRow,
} from "~/components/ui/table";
<TableRoot>
<TableCaption>A list of your recent invoices.</TableCaption>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Invoice</TableHead>
<TableHead>Status</TableHead>
<TableHead>Method</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-medium">INV001</TableCell>
<TableCell>Paid</TableCell>
<TableCell>Credit Card</TableCell>
<TableCell className="text-right">$250.00</TableCell>
</TableRow>
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total</TableCell>
<TableCell className="text-right">$2,500.00</TableCell>
</TableRow>
</TableFooter>
</TableRoot>
Examples
Sorting
Preview
Code
Invoice | Status | Method | Amount |
---|
Resizable
Preview
Code
Invoice | Status | Method | Amount |
---|---|---|---|
INV001 | Paid | Credit Card | $250.00 |
INV002 | Pending | PayPal | $150.00 |
INV003 | Unpaid | Bank Transfer | $350.00 |
INV004 | Paid | Credit Card | $450.00 |
INV005 | Paid | PayPal | $550.00 |
INV006 | Pending | Bank Transfer | $200.00 |
INV007 | Unpaid | Credit Card | $300.00 |
API Reference
Root
Prop | Type | Default |
---|---|---|
allowResize | boolean | false |
selectionBehavior | enum | "toggle" |
disabledBehavior | enum | "selection" |
selectionMode | enum | |
disallowEmptySelection | boolean |