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

A list of your recent invoices.
InvoiceStatusMethodAmount
INV001PaidCredit Card$250.00
INV002PendingPayPal$150.00
INV003UnpaidBank 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>
ProductQuantityPriceTotal
Laptop2$999.00$1998.00
Mouse5$29.99$149.95
Keyboard3$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

NameEmailStatusActions
John Doejohn@example.comActive
Jane Smithjane@example.comPending
Bob Johnsonbob@example.comInactive
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

TaskStatusPriorityDue Date
Design new login pageIn ProgressHigh2024-01-15
Fix user registration bugCompletedMedium2024-01-12
Update documentationTo DoLow2024-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>
  );
};