Github

Table

A responsive table component.

InvoiceStatusMethodAmount
INV001PaidCredit Card$250.00
INV002PendingPayPal$150.00
INV003UnpaidBank Transfer$350.00
INV004PaidCredit Card$450.00
INV005PaidPayPal$550.00
INV006PendingBank Transfer$200.00
INV007UnpaidCredit Card$300.00
import {	Table,	TableBody,	TableCell,	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>			<TableHeader>				<TableRow>					<TableHead className="w-[100px]">Invoice</TableHead>					<TableHead>Status</TableHead>					<TableHead>Method</TableHead>					<TableHead className="text-right">Amount</TableHead>				</TableRow>			</TableHeader>			<TableBody>				{invoices.map((invoice) => (					<TableRow key={invoice.invoice}>						<TableCell className="font-medium">{invoice.invoice}</TableCell>						<TableCell>{invoice.paymentStatus}</TableCell>						<TableCell>{invoice.paymentMethod}</TableCell>						<TableCell className="text-right">{invoice.totalAmount}</TableCell>					</TableRow>				))}			</TableBody>		</Table>	);}

Installation

pnpm dlx shadcn@latest add @herocn/table

Usage

import {  Table,  TableBody,  TableCaption,  TableCell,  TableFooter,  TableHead,  TableHeader,  TableRow,} from "@/components/ui/table"
<Table>  <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></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└── TableFooter

Examples

Secondary

Use variant="secondary" for a table with a different visual style.

A list of your recent invoices.
InvoiceStatusMethodAmount
INV001PaidCredit Card$250.00
INV002PendingPayPal$150.00
INV003UnpaidBank Transfer$350.00
INV004PaidCredit Card$450.00
INV005PaidPayPal$550.00
INV006PendingBank Transfer$200.00
INV007UnpaidCredit Card$300.00
import {	Table,	TableBody,	TableCaption,	TableCell,	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 TableSecondary() {	return (		<Table variant="secondary">			<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>				{invoices.map((invoice) => (					<TableRow key={invoice.invoice}>						<TableCell className="font-medium">{invoice.invoice}</TableCell>						<TableCell>{invoice.paymentStatus}</TableCell>						<TableCell>{invoice.paymentMethod}</TableCell>						<TableCell className="text-right">{invoice.totalAmount}</TableCell>					</TableRow>				))}			</TableBody>		</Table>	);}

A secondary variant table with a footer showing totals.

A list of your recent invoices.
InvoiceStatusMethodAmount
INV001PaidCredit Card$250.00
INV002PendingPayPal$150.00
INV003UnpaidBank Transfer$350.00
Total$750.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",	},];
export function TableSecondaryFooter() {	return (		<Table variant="secondary">			<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>				{invoices.map((invoice) => (					<TableRow key={invoice.invoice}>						<TableCell className="font-medium">{invoice.invoice}</TableCell>						<TableCell>{invoice.paymentStatus}</TableCell>						<TableCell>{invoice.paymentMethod}</TableCell>						<TableCell className="text-right">{invoice.totalAmount}</TableCell>					</TableRow>				))}			</TableBody>			<TableFooter>				<TableRow>					<TableCell colSpan={3}>Total</TableCell>					<TableCell className="text-right">$750.00</TableCell>				</TableRow>			</TableFooter>		</Table>	);}

Use the <TableFooter /> component to add a footer to the table.

A list of your recent invoices.
InvoiceStatusMethodAmount
INV001PaidCredit Card$250.00
INV002PendingPayPal$150.00
INV003UnpaidBank 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 className="w-[100px]">Invoice</TableHead>					<TableHead>Status</TableHead>					<TableHead>Method</TableHead>					<TableHead className="text-right">Amount</TableHead>				</TableRow>			</TableHeader>			<TableBody>				{invoices.slice(0, 3).map((invoice) => (					<TableRow key={invoice.invoice}>						<TableCell className="font-medium">{invoice.invoice}</TableCell>						<TableCell>{invoice.paymentStatus}</TableCell>						<TableCell>{invoice.paymentMethod}</TableCell>						<TableCell className="text-right">{invoice.totalAmount}</TableCell>					</TableRow>				))}			</TableBody>			<TableFooter>				<TableRow>					<TableCell colSpan={3}>Total</TableCell>					<TableCell className="text-right">$2,500.00</TableCell>				</TableRow>			</TableFooter>		</Table>	);}

Actions

A table showing actions for each row using a <DropdownMenu /> component.

ProductPriceActions
Wireless Mouse$29.99
Mechanical Keyboard$129.99
USB-C Hub$49.99
"use client";
import { MoreHorizontalIcon } from "lucide-react";
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 className="text-right">Actions</TableHead>				</TableRow>			</TableHeader>			<TableBody>				<TableRow>					<TableCell className="font-medium">Wireless Mouse</TableCell>					<TableCell>$29.99</TableCell>					<TableCell className="text-right">						<DropdownMenu>							<DropdownMenuTrigger								render={									<Button variant="ghost" size="icon" className="size-8" />								}							>								<MoreHorizontalIcon />								<span className="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 className="font-medium">Mechanical Keyboard</TableCell>					<TableCell>$129.99</TableCell>					<TableCell className="text-right">						<DropdownMenu>							<DropdownMenuTrigger								render={									<Button variant="ghost" size="icon" className="size-8" />								}							>								<MoreHorizontalIcon />								<span className="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 className="font-medium">USB-C Hub</TableCell>					<TableCell>$49.99</TableCell>					<TableCell className="text-right">						<DropdownMenu>							<DropdownMenuTrigger								render={									<Button variant="ghost" size="icon" className="size-8" />								}							>								<MoreHorizontalIcon />								<span className="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>	);}

RTL

To enable RTL support in herocn, see the RTL configuration guide.

الفاتورةالحالةالطريقةالمبلغ
INV001مدفوعبطاقة ائتمانية$250.00
INV002قيد الانتظارPayPal$150.00
INV003غير مدفوعتحويل بنكي$350.00
INV004مدفوعبطاقة ائتمانية$450.00
INV005مدفوعPayPal$550.00
INV006قيد الانتظارتحويل بنكي$200.00
INV007غير مدفوعبطاقة ائتمانية$300.00
"use client";
import {	type Translations,	useTranslation,} from "@/components/language-selector";import {	Table,	TableBody,	TableCell,	TableHead,	TableHeader,	TableRow,} from "@/components/ui/table";
const translations: Translations = {	en: {		dir: "ltr",		values: {			invoice: "Invoice",			status: "Status",			method: "Method",			amount: "Amount",			paid: "Paid",			pending: "Pending",			unpaid: "Unpaid",			creditCard: "Credit Card",			paypal: "PayPal",			bankTransfer: "Bank Transfer",		},	},	ar: {		dir: "rtl",		values: {			invoice: "الفاتورة",			status: "الحالة",			method: "الطريقة",			amount: "المبلغ",			paid: "مدفوع",			pending: "قيد الانتظار",			unpaid: "غير مدفوع",			creditCard: "بطاقة ائتمانية",			paypal: "PayPal",			bankTransfer: "تحويل بنكي",		},	},	he: {		dir: "rtl",		values: {			invoice: "חשבונית",			status: "סטטוס",			method: "שיטה",			amount: "סכום",			paid: "שולם",			pending: "ממתין",			unpaid: "לא שולם",			creditCard: "כרטיס אשראי",			paypal: "PayPal",			bankTransfer: "העברה בנקאית",		},	},};
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}>			<TableHeader>				<TableRow>					<TableHead className="w-[100px]">{t.invoice}</TableHead>					<TableHead>{t.status}</TableHead>					<TableHead>{t.method}</TableHead>					<TableHead className="text-right">{t.amount}</TableHead>				</TableRow>			</TableHeader>			<TableBody>				{invoices.map((invoice) => (					<TableRow key={invoice.invoice}>						<TableCell className="font-medium">{invoice.invoice}</TableCell>						<TableCell>{t[invoice.paymentStatus]}</TableCell>						<TableCell>{t[invoice.paymentMethod]}</TableCell>						<TableCell className="text-right">{invoice.totalAmount}</TableCell>					</TableRow>				))}			</TableBody>		</Table>	);}

API Reference

Table

PropTypeDefault
containerClassNamestring
—
classNamestring
—

TableHeader

PropTypeDefault
classNamestring
—

TableBody

PropTypeDefault
classNamestring
—

TableFooter

PropTypeDefault
classNamestring
—

TableRow

PropTypeDefault
classNamestring
—

TableHead

PropTypeDefault
classNamestring
—

TableCell

PropTypeDefault
classNamestring
—

TableCaption

PropTypeDefault
classNamestring
—