shadcn.io is not affiliated with official shadcn/ui
Shadcn Table for React and Tailwind
A responsive table component.
| Name | Role | Status | |
|---|---|---|---|
| Alice Johnson | [email protected] | Admin | active |
| Bruno Silva | [email protected] | Editor | invited |
| Clara Mendes | [email protected] | Viewer | inactive |
| David Park | [email protected] | Editor | active |
Installation
bunx --bun shadcn@latest add https://kit.dev/r/table.jsonpnpm dlx shadcn@latest add https://kit.dev/r/table.jsonnpx shadcn@latest add https://kit.dev/r/table.jsonyarn shadcn@latest add https://kit.dev/r/table.jsonInstall the following dependencies:
bun add @ark-ui/reactpnpm add @ark-ui/reactnpm install @ark-ui/reactyarn add @ark-ui/reactCopy and paste the following code into your project.
"use client";
import { ark } from "@ark-ui/react/factory";
import type React from "react";
import { cn } from "@/lib/utils";
interface TableProps extends React.ComponentProps<typeof ark.table> {
/**
* Whether the table rows are hoverable.
*
* @default true
*/
isHoverable?: boolean;
/**
* The variant of the table.
*
* @default "plain"
*/
variant?: "plain" | "striped";
}
export const Table = (props: TableProps) => {
const { variant = "plain", isHoverable = true, className, ...rest } = props;
return (
<div className="relative w-full overflow-auto" data-slot="table-wrapper">
<ark.table
className={cn(
"group/table",
"w-full",
"caption-bottom",
"text-foreground text-sm",
className
)}
data-hoverable={isHoverable}
data-slot="table"
data-variant={variant}
{...rest}
/>
</div>
);
};
export const TableHeader = (props: React.ComponentProps<typeof ark.thead>) => {
const { className, ...rest } = props;
return (
<ark.thead
className={cn("[&_tr]:border-b", className)}
data-slot="table-header"
{...rest}
/>
);
};
export interface TableBodyProps
extends React.ComponentProps<typeof ark.tbody> {}
export const TableBody = (props: TableBodyProps) => {
const { className, ...rest } = props;
return (
<ark.tbody
className={cn("[&_tr:last-child]:border-0", className)}
data-slot="table-body"
{...rest}
/>
);
};
export const TableFooter = (props: React.ComponentProps<typeof ark.tfoot>) => {
const { className, ...rest } = props;
return (
<ark.tfoot
className={cn(
"border-t",
"bg-muted/48",
"font-medium",
"last:[&>tr]:border-b-0",
className
)}
data-slot="table-footer"
{...rest}
/>
);
};
export const TableRow = (props: React.ComponentProps<typeof ark.tr>) => {
const { className, ...rest } = props;
return (
<ark.tr
className={cn(
"border-b",
"data-[state=selected]:bg-muted",
"group-data-[variant=striped]/table:even:bg-muted/30",
"group-data-[hoverable=true]/table:[&:has(td):hover]:bg-muted/48",
className
)}
data-slot="table-row"
{...rest}
/>
);
};
export const TableHead = (props: React.ComponentProps<typeof ark.th>) => {
const { className, ...rest } = props;
return (
<ark.th
className={cn(
"h-10 px-2",
"text-left align-middle",
"font-medium text-muted-foreground",
"rtl:text-right",
"has-[[role=checkbox]]:ps-2 has-[[role=checkbox]]:pe-0",
className
)}
data-slot="table-head"
{...rest}
/>
);
};
export const TableCell = (props: React.ComponentProps<typeof ark.td>) => {
const { className, ...rest } = props;
return (
<ark.td
className={cn(
"whitespace-nowrap p-2 align-middle",
"has-[[role=checkbox]]:ps-2 has-[[role=checkbox]]:pe-0",
className
)}
data-slot="table-cell"
{...rest}
/>
);
};
export const TableCaption = (
props: React.ComponentProps<typeof ark.caption>
) => {
const { className, ...rest } = props;
return (
<ark.caption
className={cn("mt-4", "text-muted-foreground text-sm", className)}
data-slot="table-caption"
{...rest}
/>
);
};Update the import paths to match your project setup.
Usage
import { Badge, type BadgeVariant } from "@/components/ui/badge";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";const TableDemo = () => (
<Table className="mx-auto w-full max-w-xl">
<TableCaption>A list of users in your workspace.</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Email</TableHead>
<TableHead>Role</TableHead>
<TableHead className="text-center">Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{users.map((user) => (
<TableRow key={user.id}>
<TableCell>{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell>{user.role}</TableCell>
<TableCell className="text-center">
<Badge className="capitalize" variant={statusVariants[user.status]}>
{user.status}
</Badge>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
const statusVariants: Record<string, BadgeVariant> = {
active: "success",
invited: "info",
inactive: "destructive",
};
const users = [
{
id: "1",
name: "Alice Johnson",
email: "[email protected]",
role: "Admin",
status: "active",
},
{
id: "2",
name: "Bruno Silva",
email: "[email protected]",
role: "Editor",
status: "invited",
},
{
id: "3",
name: "Clara Mendes",
email: "[email protected]",
role: "Viewer",
status: "inactive",
},
{
id: "4",
name: "David Park",
email: "[email protected]",
role: "Editor",
status: "active",
},
];Demos
Action Bar
Actions
Context Menu
Footer
Not Hoverable
Striped
API Reference
Table
table
PropType
AttributeType
TableHeader
thead
PropType
AttributeType
TableBody
tbody
PropType
AttributeType
TableFooter
tfoot
PropType
AttributeType
TableRow
tr
PropType
AttributeType
TableHead
th
PropType
AttributeType
TableCell
td
PropType
AttributeType
TableCaption
caption
PropType
AttributeType