Table
A responsive table component for displaying tabular data.
The Table component provides semantic HTML table elements with consistent styling. It includes support for headers, bodies, footers, and captions. Built from scratch using React and native HTML table elements. No dependencies on any UI library.
Code
TypeScript: Copy this code into components/ui/table.tsx:
tsx
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
))
Table.displayName = "Table"
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead
ref={ref}
className={cn("[&_tr]:border-b-2 [&_tr]:border-foreground", className)}
{...props}
/>
))
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
))
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn(
"border-t-2 border-foreground bg-muted font-bold [&>tr]:last:border-b-0",
className
)}
{...props}
/>
))
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b-2 border-foreground transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...props}
/>
))
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-12 px-4 text-left align-middle font-bold [&:has([role=checkbox])]:pr-0",
className
)}
{...props}
/>
))
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
))
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
))
TableCaption.displayName = "TableCaption"
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
}JavaScript: Copy this code into components/ui/table.jsx:
jsx
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
const Table = React.forwardRef(
({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
)
)
Table.displayName = "Table"
const TableHeader = React.forwardRef(
({ className, ...props }, ref) => (
<thead
ref={ref}
className={cn("[&_tr]:border-b-2 [&_tr]:border-foreground", className)}
{...props}
/>
)
)
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef(
({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
)
)
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef(
({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn(
"border-t-2 border-foreground bg-muted font-bold [&>tr]:last:border-b-0",
className
)}
{...props}
/>
)
)
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef(
({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b-2 border-foreground transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...props}
/>
)
)
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef(
({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-12 px-4 text-left align-middle font-bold [&:has([role=checkbox])]:pr-0",
className
)}
{...props}
/>
)
)
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef(
({ className, ...props }, ref) => (
<td
ref={ref}
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
)
)
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef(
({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
)
)
TableCaption.displayName = "TableCaption"
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
}Usage
TypeScript:
tsx
import {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
function MyComponent() {
return (
<Table>
<TableCaption>A list of your recent invoices.</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Invoice</TableHead>
<TableHead>Status</TableHead>
<TableHead>Method</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>INV001</TableCell>
<TableCell>Paid</TableCell>
<TableCell>Credit Card</TableCell>
<TableCell className="text-right">$250.00</TableCell>
</TableRow>
<TableRow>
<TableCell>INV002</TableCell>
<TableCell>Pending</TableCell>
<TableCell>PayPal</TableCell>
<TableCell className="text-right">$150.00</TableCell>
</TableRow>
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total</TableCell>
<TableCell className="text-right">$2,500.00</TableCell>
</TableRow>
</TableFooter>
</Table>
)
}JavaScript:
jsx
import {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
function MyComponent() {
return (
<Table>
<TableCaption>A list of your recent invoices.</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Invoice</TableHead>
<TableHead>Status</TableHead>
<TableHead>Method</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>INV001</TableCell>
<TableCell>Paid</TableCell>
<TableCell>Credit Card</TableCell>
<TableCell className="text-right">$250.00</TableCell>
</TableRow>
<TableRow>
<TableCell>INV002</TableCell>
<TableCell>Pending</TableCell>
<TableCell>PayPal</TableCell>
<TableCell className="text-right">$150.00</TableCell>
</TableRow>
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total</TableCell>
<TableCell className="text-right">$2,500.00</TableCell>
</TableRow>
</TableFooter>
</Table>
)
}Make sure you also have the lib/utils.ts file with the cn helper function.
Examples
Default Table
| 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,250.00 | ||