Table
A flexible table component for displaying structured data in rows and columns.
Types
interface TableProps extends ComponentProps<"table"> {}
interface TableHeaderProps extends ComponentProps<"thead"> {}
interface TableBodyProps extends ComponentProps<"tbody"> {}
interface TableFooterProps extends ComponentProps<"tfoot"> {}
interface TableRowProps extends ComponentProps<"tr"> {}
interface TableHeadProps extends ComponentProps<"th"> {}
interface TableCellProps extends ComponentProps<"td"> {}
interface TableCaptionProps extends ComponentProps<"caption"> {}
Usage
| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV001 | Paid | Credit Card | $250.00 |
| INV002 | Pending | PayPal | $150.00 |
| INV003 | Unpaid | Bank Transfer | $350.00 |
const invoices = [
{ id: "INV001", status: "Paid", method: "Credit Card", amount: "$250.00" },
{ id: "INV002", status: "Pending", method: "PayPal", amount: "$150.00" },
{ id: "INV003", status: "Unpaid", method: "Bank Transfer", amount: "$350.00" },
{ id: "INV004", status: "Paid", method: "Credit Card", amount: "$450.00" },
{ id: "INV005", status: "Paid", method: "PayPal", amount: "$550.00" },
{ id: "INV006", status: "Pending", method: "Bank Transfer", amount: "$200.00" },
{ id: "INV007", status: "Unpaid", method: "Credit Card", amount: "$300.00" },
];
<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.map((invoice) => (
<TableRow key={invoice.id}>
<TableCell className="font-medium">{invoice.id}</TableCell>
<TableCell>{invoice.status}</TableCell>
<TableCell>{invoice.method}</TableCell>
<TableCell className="text-right">{invoice.amount}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
With Footer
| Product | Quantity | Price | Total |
|---|---|---|---|
| Laptop | 2 | $999.00 | $1998.00 |
| Mouse | 5 | $29.99 | $149.95 |
| Keyboard | 3 | $79.99 | $239.97 |
| Total | $2387.92 | ||
const products = [
{ name: "Laptop", quantity: 2, price: 999.00 },
{ name: "Mouse", quantity: 5, price: 29.99 },
{ name: "Keyboard", quantity: 3, price: 79.99 },
];
const total = products.reduce((sum, product) =>
sum + (product.quantity * product.price), 0
);
<Table>
<TableHeader>
<TableRow>
<TableHead>Product</TableHead>
<TableHead>Quantity</TableHead>
<TableHead>Price</TableHead>
<TableHead className="text-right">Total</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{products.map((product) => (
<TableRow key={product.name}>
<TableCell className="font-medium">{product.name}</TableCell>
<TableCell>{product.quantity}</TableCell>
<TableCell>${product.price.toFixed(2)}</TableCell>
<TableCell className="text-right">
${(product.quantity * product.price).toFixed(2)}
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total</TableCell>
<TableCell className="text-right">${total.toFixed(2)}</TableCell>
</TableRow>
</TableFooter>
</Table>
With Actions
| Name | Status | Actions | |
|---|---|---|---|
| John Doe | john@example.com | Active | |
| Jane Smith | jane@example.com | Pending | |
| Bob Johnson | bob@example.com | Inactive |
const users = [
{ id: 1, name: "John Doe", email: "john@example.com", status: "Active" },
{ id: 2, name: "Jane Smith", email: "jane@example.com", status: "Pending" },
{ id: 3, name: "Bob Johnson", email: "bob@example.com", status: "Inactive" },
];
const getStatusVariant = (status) => {
switch (status) {
case "Active": return "success";
case "Pending": return "secondary";
case "Inactive": return "destructive";
default: return "default";
}
};
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Email</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{users.map((user) => (
<TableRow key={user.id}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell>
<Badge variant={getStatusVariant(user.status)}>{user.status}</Badge>
</TableCell>
<TableCell className="text-right">
<Button variant="ghost" size="sm">
Edit
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
With Selection
| Task | Status | Priority | Due Date | |
|---|---|---|---|---|
| Design new login page | In Progress | High | 2024-01-15 | |
| Fix user registration bug | Completed | Medium | 2024-01-12 | |
| Update documentation | To Do | Low | 2024-01-20 |
import { useState } from 'react';
const tasks = [
{ id: 1, task: "Design new login page", status: "In Progress", priority: "High", dueDate: "2024-01-15" },
{ id: 2, task: "Fix user registration bug", status: "Completed", priority: "Medium", dueDate: "2024-01-12" },
{ id: 3, task: "Update documentation", status: "To Do", priority: "Low", dueDate: "2024-01-20" },
];
const SelectableTable = () => {
const [selectedTasks, setSelectedTasks] = useState([]);
const handleSelectAll = (checked) => {
if (checked) {
setSelectedTasks(tasks.map(task => task.id));
} else {
setSelectedTasks([]);
}
};
const handleSelectTask = (taskId, checked) => {
if (checked) {
setSelectedTasks([...selectedTasks, taskId]);
} else {
setSelectedTasks(selectedTasks.filter(id => id !== taskId));
}
};
const isAllSelected = selectedTasks.length === tasks.length;
const isIndeterminate = selectedTasks.length > 0 && selectedTasks.length < tasks.length;
return (
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-12">
<Checkbox
checkall
checked={isAllSelected}
indeterminate={isIndeterminate}
onCheckedChange={handleSelectAll}
/>
</TableHead>
<TableHead>Task</TableHead>
<TableHead>Status</TableHead>
<TableHead>Priority</TableHead>
<TableHead>Due Date</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{tasks.map((task) => (
<TableRow
key={task.id}
data-state={selectedTasks.includes(task.id) ? "selected" : undefined}
>
<TableCell>
<Checkbox
checked={selectedTasks.includes(task.id)}
onCheckedChange={(checked) => handleSelectTask(task.id, checked)}
/>
</TableCell>
<TableCell className="font-medium">{task.task}</TableCell>
<TableCell>
<Badge variant={getStatusBadgeVariant(task.status)}>{task.status}</Badge>
</TableCell>
<TableCell>
<Badge variant={getPriorityBadgeVariant(task.priority)}>{task.priority}</Badge>
</TableCell>
<TableCell>{task.dueDate}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
};