About
A styled, responsive table primitive with default and card variants. Ships Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell and TableCaption — semantic <table> elements with hover and selected row states, a horizontal-scroll container, and a rounded 'card' layout. Dependency-free (just cn). Compose it for project lists, invoices, inventory, API metrics, server usage, subscriptions or pricing comparisons.
Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Table to your project and fits it to your theme.
Code
How Table is used: the demo's code. The component's own source comes with a free account.
Usage.tsx
"use client";
import { ArrowDownIcon, ArrowUpDownIcon, ArrowUpIcon } from "lucide-react";
import { useState } from "react";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/cnippet-table";
import type React from "react";
const badgeStyles: Record<string, string> = {
outline: "text-foreground",
default: "border-transparent bg-primary text-primary-foreground",
success:
"border-transparent bg-emerald-500/15 text-emerald-600 dark:text-emerald-400",
warning:
"border-transparent bg-amber-500/15 text-amber-600 dark:text-amber-400",
destructive: "border-transparent bg-destructive/15 text-destructive",
info: "border-transparent bg-blue-500/15 text-blue-600 dark:text-blue-400",
};
function Badge({
variant = "outline",
className = "",
children,
}: {
variant?: string;
className?: string;
children?: React.ReactNode;
}): React.ReactElement {
return (
<span
className={`inline-flex items-center gap-1.5 whitespace-nowrap rounded-md border px-2 py-0.5 font-medium text-xs [&_svg]:size-3 ${badgeStyles[variant] ?? ""} ${className}`}
>
{children}
</span>
);
}
type SortKey = "endpoint" | "latency" | "requests" | "errorRate";
type SortDir = "asc" | "desc";
const methodVariant: Record<
string,
"success" | "info" | "warning" | "destructive"
> = {
DELETE: "destructive",
GET: "success",
PATCH: "warning",
POST: "info",
PUT: "warning",
};
const rows = [
{
endpoint: "/api/users",
errorRate: 0.2,
latency: 48,
method: "GET",
requests: 12400,
},
{
endpoint: "/api/orders",
errorRate: 1.4,
latency: 134,
method: "POST",
requests: 3200,
},
{
endpoint: "/api/products",
errorRate: 0.0,
latency: 22,
method: "GET",
requests: 28900,
},
{
endpoint: "/api/auth/login",
errorRate: 3.8,
latency: 210,
method: "POST",
requests: 8700,
},
{
endpoint: "/api/users/:id",
errorRate: 0.5,
latency: 61,
method: "PATCH",
requests: 1100,
},
{
endpoint: "/api/orders/:id",
errorRate: 0.0,
latency: 38,
method: "DELETE",
requests: 540,
},
];
function SortIcon({
col,
sortKey,
sortDir,
}: {
col: SortKey;
sortKey: SortKey;
sortDir: SortDir;
}) {
if (col !== sortKey) return <ArrowUpDownIcon className="size-3 opacity-40" />;
return sortDir === "asc" ? (
<ArrowUpIcon className="size-3" />
) : (
<ArrowDownIcon className="size-3" />
);
}
export default function TableApi() {
const [sortKey, setSortKey] = useState<SortKey>("requests");
const [sortDir, setSortDir] = useState<SortDir>("desc");
const sorted = [...rows].sort((a, b) => {
const val = sortDir === "asc" ? 1 : -1;
if (typeof a[sortKey] === "string") {
return val * (a[sortKey] as string).localeCompare(b[sortKey] as string);
}
return val * ((a[sortKey] as number) - (b[sortKey] as number));
});
const toggleSort = (key: SortKey) => {
if (sortKey === key) setSortDir((d) => (d === "asc" ? "desc" : "asc"));
else {
setSortKey(key);
setSortDir("desc");
}
};
const th = (key: SortKey, label: string, align = "left") => (
<TableHead
className={`cursor-pointer select-none transition-colors hover:text-foreground ${align === "right" ? "text-right" : ""}`}
onClick={() => toggleSort(key)}
>
<span
className={`inline-flex items-center gap-1 ${align === "right" ? "flex-row-reverse" : ""}`}
>
{label}
<SortIcon col={key} sortDir={sortDir} sortKey={sortKey} />
</span>
</TableHead>
);
return (
<div className="w-full max-w-2xl">
<Table>
<TableHeader>
<TableRow>
<TableHead>Method</TableHead>
{th("endpoint", "Endpoint")}
{th("latency", "Avg latency", "right")}
{th("requests", "Req / day", "right")}
{th("errorRate", "Error %", "right")}
</TableRow>
</TableHeader>
<TableBody>
{sorted.map((row) => (
<TableRow key={`${row.method}-${row.endpoint}`}>
<TableCell>
<Badge size="sm" variant={methodVariant[row.method]}>
{row.method}
</Badge>
</TableCell>
<TableCell className="font-mono text-xs">
{row.endpoint}
</TableCell>
<TableCell className="text-right text-sm">
{row.latency} ms
</TableCell>
<TableCell className="text-right text-sm">
{row.requests.toLocaleString()}
</TableCell>
<TableCell className="text-right">
<span
className={`font-medium text-sm ${
row.errorRate >= 3
? "text-destructive"
: row.errorRate >= 1
? "text-warning"
: "text-success-foreground"
}`}
>
{row.errorRate.toFixed(1)}%
</span>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}








