Get Started
Components
- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Card
- Checkbox
- Collapsible
- Combobox
- Context Menu
- Date Picker (Lite)
- Dialog
- Direction
- Drawer
- Dropdown Menu
- Empty
- Field
- Hover Card
- Input
- Input Group
- Input OTP (Lite)
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Native Select
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Toggle Group
- Tooltip
| 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 |
| Total | $2,500.00 | ||
import {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
const invoices = [
{
invoice: "INV001",
paymentStatus: "Paid",
totalAmount: "$250.00",
paymentMethod: "Credit Card",
},
{
invoice: "INV002",
paymentStatus: "Pending",
totalAmount: "$150.00",
paymentMethod: "PayPal",
},
{
invoice: "INV003",
paymentStatus: "Unpaid",
totalAmount: "$350.00",
paymentMethod: "Bank Transfer",
},
{
invoice: "INV004",
paymentStatus: "Paid",
totalAmount: "$450.00",
paymentMethod: "Credit Card",
},
{
invoice: "INV005",
paymentStatus: "Paid",
totalAmount: "$550.00",
paymentMethod: "PayPal",
},
{
invoice: "INV006",
paymentStatus: "Pending",
totalAmount: "$200.00",
paymentMethod: "Bank Transfer",
},
{
invoice: "INV007",
paymentStatus: "Unpaid",
totalAmount: "$300.00",
paymentMethod: "Credit Card",
},
]
export function TableDemo() {
return (
<Table>
<TableCaption>A list of your recent invoices.</TableCaption>
<TableHeader>
<TableRow>
<TableHead class="w-[100px]">Invoice</TableHead>
<TableHead>Status</TableHead>
<TableHead>Method</TableHead>
<TableHead class="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{invoices.map((invoice) => (
<TableRow key={invoice.invoice}>
<TableCell class="font-medium">{invoice.invoice}</TableCell>
<TableCell>{invoice.paymentStatus}</TableCell>
<TableCell>{invoice.paymentMethod}</TableCell>
<TableCell class="text-right">{invoice.totalAmount}</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total</TableCell>
<TableCell class="text-right">$2,500.00</TableCell>
</TableRow>
</TableFooter>
</Table>
)
}Installation
pnpm dlx shadcnui-hono-jsx@latest add tablenpx shadcnui-hono-jsx@latest add tableyarn dlx shadcnui-hono-jsx@latest add tablebunx shadcnui-hono-jsx@latest add tableUsage
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"<Table>
<TableCaption>A list of your recent invoices.</TableCaption>
<TableHeader>
<TableRow>
<TableHead class="w-[100px]">Invoice</TableHead>
<TableHead>Status</TableHead>
<TableHead>Method</TableHead>
<TableHead class="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell class="font-medium">INV001</TableCell>
<TableCell>Paid</TableCell>
<TableCell>Credit Card</TableCell>
<TableCell class="text-right">$250.00</TableCell>
</TableRow>
</TableBody>
</Table>Composition
Use the following composition to build a Table:
Table
├── TableCaption
├── TableHeader
│ └── TableRow
│ ├── TableHead
│ ├── TableHead
│ ├── TableHead
│ └── TableHead
├── TableBody
│ ├── TableRow
│ │ ├── TableCell
│ │ ├── TableCell
│ │ ├── TableCell
│ │ └── TableCell
│ └── TableRow
│ ├── TableCell
│ ├── TableCell
│ ├── TableCell
│ └── TableCell
└── TableFooterFooter
Use the <TableFooter /> component to add a footer to the table.
| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV001 | Paid | Credit Card | $250.00 |
| INV002 | Pending | PayPal | $150.00 |
| INV003 | Unpaid | Bank Transfer | $350.00 |
| Total | $2,500.00 | ||
import {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
const invoices = [
{
invoice: "INV001",
paymentStatus: "Paid",
totalAmount: "$250.00",
paymentMethod: "Credit Card",
},
{
invoice: "INV002",
paymentStatus: "Pending",
totalAmount: "$150.00",
paymentMethod: "PayPal",
},
{
invoice: "INV003",
paymentStatus: "Unpaid",
totalAmount: "$350.00",
paymentMethod: "Bank Transfer",
},
{
invoice: "INV004",
paymentStatus: "Paid",
totalAmount: "$450.00",
paymentMethod: "Credit Card",
},
{
invoice: "INV005",
paymentStatus: "Paid",
totalAmount: "$550.00",
paymentMethod: "PayPal",
},
{
invoice: "INV006",
paymentStatus: "Pending",
totalAmount: "$200.00",
paymentMethod: "Bank Transfer",
},
{
invoice: "INV007",
paymentStatus: "Unpaid",
totalAmount: "$300.00",
paymentMethod: "Credit Card",
},
]
export function TableFooterExample() {
return (
<Table>
<TableCaption>A list of your recent invoices.</TableCaption>
<TableHeader>
<TableRow>
<TableHead class="w-[100px]">Invoice</TableHead>
<TableHead>Status</TableHead>
<TableHead>Method</TableHead>
<TableHead class="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{invoices.slice(0, 3).map((invoice) => (
<TableRow key={invoice.invoice}>
<TableCell class="font-medium">{invoice.invoice}</TableCell>
<TableCell>{invoice.paymentStatus}</TableCell>
<TableCell>{invoice.paymentMethod}</TableCell>
<TableCell class="text-right">{invoice.totalAmount}</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total</TableCell>
<TableCell class="text-right">$2,500.00</TableCell>
</TableRow>
</TableFooter>
</Table>
)
}Actions
A table showing actions for each row using a <DropdownMenu /> component.
| Product | Price | Actions |
|---|---|---|
| Wireless Mouse | $29.99 | Edit Duplicate Delete |
| Mechanical Keyboard | $129.99 | Edit Duplicate Delete |
| USB-C Hub | $49.99 | Edit Duplicate Delete |
import { MoreHorizontalIcon } from "@/components/icons"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
export function TableActions() {
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>Product</TableHead>
<TableHead>Price</TableHead>
<TableHead class="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell class="font-medium">Wireless Mouse</TableCell>
<TableCell>$29.99</TableCell>
<TableCell class="text-right">
<DropdownMenu>
<DropdownMenuTrigger
render={<Button variant="ghost" size="icon" class="size-8" />}
>
<MoreHorizontalIcon />
<span class="sr-only">Open menu</span>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Duplicate</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive">
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
<TableRow>
<TableCell class="font-medium">Mechanical Keyboard</TableCell>
<TableCell>$129.99</TableCell>
<TableCell class="text-right">
<DropdownMenu>
<DropdownMenuTrigger
render={<Button variant="ghost" size="icon" class="size-8" />}
>
<MoreHorizontalIcon />
<span class="sr-only">Open menu</span>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Duplicate</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive">
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
<TableRow>
<TableCell class="font-medium">USB-C Hub</TableCell>
<TableCell>$49.99</TableCell>
<TableCell class="text-right">
<DropdownMenu>
<DropdownMenuTrigger
render={<Button variant="ghost" size="icon" class="size-8" />}
>
<MoreHorizontalIcon />
<span class="sr-only">Open menu</span>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Duplicate</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive">
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
</TableBody>
</Table>
)
}Data Table
You can use the <Table /> component to build more complex data tables. Combine it with @tanstack/react-table to create tables with sorting, filtering and pagination.
See the Data Table documentation for more information.
You can also see an example of a data table in the Tasks demo.
RTL
To enable RTL support in shadcn/ui, see the RTL configuration guide.
| 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 |
| Total | $2,500.00 | ||
| الفاتورة | الحالة | الطريقة | المبلغ |
|---|---|---|---|
| INV001 | مدفوع | بطاقة ائتمانية | $250.00 |
| INV002 | قيد الانتظار | PayPal | $150.00 |
| INV003 | غير مدفوع | تحويل بنكي | $350.00 |
| INV004 | مدفوع | بطاقة ائتمانية | $450.00 |
| INV005 | مدفوع | PayPal | $550.00 |
| INV006 | قيد الانتظار | تحويل بنكي | $200.00 |
| INV007 | غير مدفوع | بطاقة ائتمانية | $300.00 |
| المجموع | $2,500.00 | ||
| חשבונית | סטטוס | שיטה | סכום |
|---|---|---|---|
| INV001 | שולם | כרטיס אשראי | $250.00 |
| INV002 | ממתין | PayPal | $150.00 |
| INV003 | לא שולם | העברה בנקאית | $350.00 |
| INV004 | שולם | כרטיס אשראי | $450.00 |
| INV005 | שולם | PayPal | $550.00 |
| INV006 | ממתין | העברה בנקאית | $200.00 |
| INV007 | לא שולם | כרטיס אשראי | $300.00 |
| סה"כ | $2,500.00 | ||
// This example sets dir and uses useTranslation because this site is not RTL.
// In an RTL app you won't need them; see the RTL guide.
import {
type Translations,
useTranslation,
} from "@/components/language-selector"
import {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
const translations: Translations = {
en: {
dir: "ltr",
values: {
caption: "A list of your recent invoices.",
invoice: "Invoice",
status: "Status",
method: "Method",
amount: "Amount",
paid: "Paid",
pending: "Pending",
unpaid: "Unpaid",
creditCard: "Credit Card",
paypal: "PayPal",
bankTransfer: "Bank Transfer",
total: "Total",
},
},
ar: {
dir: "rtl",
values: {
caption: "قائمة بفواتيرك الأخيرة.",
invoice: "الفاتورة",
status: "الحالة",
method: "الطريقة",
amount: "المبلغ",
paid: "مدفوع",
pending: "قيد الانتظار",
unpaid: "غير مدفوع",
creditCard: "بطاقة ائتمانية",
paypal: "PayPal",
bankTransfer: "تحويل بنكي",
total: "المجموع",
},
},
he: {
dir: "rtl",
values: {
caption: "רשימת החשבוניות האחרונות שלך.",
invoice: "חשבונית",
status: "סטטוס",
method: "שיטה",
amount: "סכום",
paid: "שולם",
pending: "ממתין",
unpaid: "לא שולם",
creditCard: "כרטיס אשראי",
paypal: "PayPal",
bankTransfer: "העברה בנקאית",
total: 'סה"כ',
},
},
}
const invoices = [
{
invoice: "INV001",
paymentStatus: "paid" as const,
totalAmount: "$250.00",
paymentMethod: "creditCard" as const,
},
{
invoice: "INV002",
paymentStatus: "pending" as const,
totalAmount: "$150.00",
paymentMethod: "paypal" as const,
},
{
invoice: "INV003",
paymentStatus: "unpaid" as const,
totalAmount: "$350.00",
paymentMethod: "bankTransfer" as const,
},
{
invoice: "INV004",
paymentStatus: "paid" as const,
totalAmount: "$450.00",
paymentMethod: "creditCard" as const,
},
{
invoice: "INV005",
paymentStatus: "paid" as const,
totalAmount: "$550.00",
paymentMethod: "paypal" as const,
},
{
invoice: "INV006",
paymentStatus: "pending" as const,
totalAmount: "$200.00",
paymentMethod: "bankTransfer" as const,
},
{
invoice: "INV007",
paymentStatus: "unpaid" as const,
totalAmount: "$300.00",
paymentMethod: "creditCard" as const,
},
]
export function TableRtl() {
const { dir, t } = useTranslation(translations, "ar")
return (
<Table dir={dir}>
<TableCaption>{t.caption}</TableCaption>
<TableHeader>
<TableRow>
<TableHead class="w-[100px]">{t.invoice}</TableHead>
<TableHead>{t.status}</TableHead>
<TableHead>{t.method}</TableHead>
<TableHead class="text-right">{t.amount}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{invoices.map((invoice) => (
<TableRow key={invoice.invoice}>
<TableCell class="font-medium">{invoice.invoice}</TableCell>
<TableCell>{t[invoice.paymentStatus]}</TableCell>
<TableCell>{t[invoice.paymentMethod]}</TableCell>
<TableCell class="text-right">{invoice.totalAmount}</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>{t.total}</TableCell>
<TableCell class="text-right">$2,500.00</TableCell>
</TableRow>
</TableFooter>
</Table>
)
}Notes
- Accepts
classinstead ofclassName. - Selectors for checkbox and radio roles (
[role=checkbox]) match the generated controls bydata-slot, as their roots carry no role. - Upstream documentation: shadcn/ui Table. See also Differences from shadcn/ui.