Table
A simple table component for displaying tabular data. Built with Base UI and Tailwind CSS. Copy-paste ready.
| Project | Status | Team | Budget |
|---|---|---|---|
| Website Redesign | Paid | Frontend Team | $12,500 |
| Mobile App | Unpaid | Mobile Team | $8,750 |
| API Integration | Pending | Backend Team | $5,200 |
| Database Migration | Paid | DevOps Team | $3,800 |
| User Dashboard | Paid | UX Team | $7,200 |
| Security Audit | Failed | Security Team | $2,100 |
| Total Budget | $39,550 | ||
import { Badge } from "@/components/ui/badge";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
export default function Particle() {
return (
<Table className="w-full">
<TableCaption>A list of current projects.</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Project</TableHead>
<TableHead>Status</TableHead>
<TableHead>Team</TableHead>
<TableHead className="text-right">Budget</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-medium">Website Redesign</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-emerald-500"
/>
Paid
</Badge>
</TableCell>
<TableCell>Frontend Team</TableCell>
<TableCell className="text-right">$12,500</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Mobile App</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-muted-foreground/64"
/>
Unpaid
</Badge>
</TableCell>
<TableCell>Mobile Team</TableCell>
<TableCell className="text-right">$8,750</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">API Integration</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-amber-500"
/>
Pending
</Badge>
</TableCell>
<TableCell>Backend Team</TableCell>
<TableCell className="text-right">$5,200</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Database Migration</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-emerald-500"
/>
Paid
</Badge>
</TableCell>
<TableCell>DevOps Team</TableCell>
<TableCell className="text-right">$3,800</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">User Dashboard</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-emerald-500"
/>
Paid
</Badge>
</TableCell>
<TableCell>UX Team</TableCell>
<TableCell className="text-right">$7,200</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Security Audit</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-red-500"
/>
Failed
</Badge>
</TableCell>
<TableCell>Security Team</TableCell>
<TableCell className="text-right">$2,100</TableCell>
</TableRow>
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total Budget</TableCell>
<TableCell className="text-right">$39,550</TableCell>
</TableRow>
</TableFooter>
</Table>
);
}
Installation
pnpm dlx shadcn@latest add @cnippet/table
Usage
import {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"<Table>
<TableCaption>Caption</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Header</TableHead>
<TableHead>Header</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>Cell</TableCell>
<TableCell>Cell</TableCell>
</TableRow>
</TableBody>
</Table>Examples
Card-style table
Use variant="card" when the grid itself should look like a set of cards (for example dashboards or standalone tables without an outer frame).
| Project | Status | Team | Budget |
|---|---|---|---|
| Website Redesign | Paid | Frontend Team | $12,500 |
| Mobile App | Unpaid | Mobile Team | $8,750 |
| API Integration | Pending | Backend Team | $5,200 |
| Database Migration | Paid | DevOps Team | $3,800 |
| User Dashboard | Paid | UX Team | $7,200 |
| Security Audit | Failed | Security Team | $2,100 |
| Total Budget | $39,550 | ||
import { Badge } from "@/components/ui/badge";
import {
Table,
TableBody,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
export default function Particle() {
return (
<Table className="w-full max-w-full" variant="card">
<TableHeader>
<TableRow>
<TableHead>Project</TableHead>
<TableHead>Status</TableHead>
<TableHead>Team</TableHead>
<TableHead className="text-right">Budget</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-medium">Website Redesign</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-emerald-500"
/>
Paid
</Badge>
</TableCell>
<TableCell>Frontend Team</TableCell>
<TableCell className="text-right">$12,500</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Mobile App</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-muted-foreground/64"
/>
Unpaid
</Badge>
</TableCell>
<TableCell>Mobile Team</TableCell>
<TableCell className="text-right">$8,750</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">API Integration</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-amber-500"
/>
Pending
</Badge>
</TableCell>
<TableCell>Backend Team</TableCell>
<TableCell className="text-right">$5,200</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Database Migration</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-emerald-500"
/>
Paid
</Badge>
</TableCell>
<TableCell>DevOps Team</TableCell>
<TableCell className="text-right">$3,800</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">User Dashboard</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-emerald-500"
/>
Paid
</Badge>
</TableCell>
<TableCell>UX Team</TableCell>
<TableCell className="text-right">$7,200</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Security Audit</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-red-500"
/>
Failed
</Badge>
</TableCell>
<TableCell>Security Team</TableCell>
<TableCell className="text-right">$2,100</TableCell>
</TableRow>
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total Budget</TableCell>
<TableCell className="text-right">$39,550</TableCell>
</TableRow>
</TableFooter>
</Table>
);
}
Table in CardFrame
Put the table in CardFrame so the grid sits inside the card shell (border, radius, clipping). Use variant="card" on Table. The example below is static markup—no row selection or TanStack.
| Project | Status | Team | Budget |
|---|---|---|---|
| Website Redesign | Paid | Frontend Team | $12,500 |
| Mobile App | Unpaid | Mobile Team | $8,750 |
| API Integration | Pending | Backend Team | $5,200 |
| Database Migration | Paid | DevOps Team | $3,800 |
| User Dashboard | Paid | UX Team | $7,200 |
| Security Audit | Failed | Security Team | $2,100 |
| Total Budget | $39,550 | ||
import { Badge } from "@/components/ui/badge";
import { CardFrame } from "@/components/ui/card";
import {
Table,
TableBody,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
export default function Particle() {
return (
<CardFrame className="w-full">
<Table variant="card">
<TableHeader>
<TableRow>
<TableHead>Project</TableHead>
<TableHead>Status</TableHead>
<TableHead>Team</TableHead>
<TableHead className="text-right">Budget</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-medium">Website Redesign</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-emerald-500"
/>
Paid
</Badge>
</TableCell>
<TableCell>Frontend Team</TableCell>
<TableCell className="text-right">$12,500</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Mobile App</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-muted-foreground/64"
/>
Unpaid
</Badge>
</TableCell>
<TableCell>Mobile Team</TableCell>
<TableCell className="text-right">$8,750</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">API Integration</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-amber-500"
/>
Pending
</Badge>
</TableCell>
<TableCell>Backend Team</TableCell>
<TableCell className="text-right">$5,200</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Database Migration</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-emerald-500"
/>
Paid
</Badge>
</TableCell>
<TableCell>DevOps Team</TableCell>
<TableCell className="text-right">$3,800</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">User Dashboard</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-emerald-500"
/>
Paid
</Badge>
</TableCell>
<TableCell>UX Team</TableCell>
<TableCell className="text-right">$7,200</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Security Audit</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-red-500"
/>
Failed
</Badge>
</TableCell>
<TableCell>Security Team</TableCell>
<TableCell className="text-right">$2,100</TableCell>
</TableRow>
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total Budget</TableCell>
<TableCell className="text-right">$39,550</TableCell>
</TableRow>
</TableFooter>
</Table>
</CardFrame>
);
}
Table in Frame
Wrap the table in a Frame for bordered app-surface framing. Use variant="card" on Table so rows keep the card-style treatment inside the frame.
| Project | Status | Team | Budget |
|---|---|---|---|
| Website Redesign | Paid | Frontend Team | $12,500 |
| Mobile App | Unpaid | Mobile Team | $8,750 |
| API Integration | Pending | Backend Team | $5,200 |
| Database Migration | Paid | DevOps Team | $3,800 |
| User Dashboard | Paid | UX Team | $7,200 |
| Security Audit | Failed | Security Team | $2,100 |
| Total Budget | $39,550 | ||
import { Badge } from "@/components/ui/badge";
import { Frame } from "@/components/ui/frame";
import {
Table,
TableBody,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
export default function Particle() {
return (
<Frame className="w-full">
<Table variant="card">
<TableHeader>
<TableRow>
<TableHead>Project</TableHead>
<TableHead>Status</TableHead>
<TableHead>Team</TableHead>
<TableHead className="text-right">Budget</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-medium">Website Redesign</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-emerald-500"
/>
Paid
</Badge>
</TableCell>
<TableCell>Frontend Team</TableCell>
<TableCell className="text-right">$12,500</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Mobile App</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-muted-foreground/64"
/>
Unpaid
</Badge>
</TableCell>
<TableCell>Mobile Team</TableCell>
<TableCell className="text-right">$8,750</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">API Integration</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-amber-500"
/>
Pending
</Badge>
</TableCell>
<TableCell>Backend Team</TableCell>
<TableCell className="text-right">$5,200</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Database Migration</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-emerald-500"
/>
Paid
</Badge>
</TableCell>
<TableCell>DevOps Team</TableCell>
<TableCell className="text-right">$3,800</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">User Dashboard</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-emerald-500"
/>
Paid
</Badge>
</TableCell>
<TableCell>UX Team</TableCell>
<TableCell className="text-right">$7,200</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Security Audit</TableCell>
<TableCell>
<Badge variant="outline">
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-red-500"
/>
Failed
</Badge>
</TableCell>
<TableCell>Security Team</TableCell>
<TableCell className="text-right">$2,100</TableCell>
</TableRow>
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total Budget</TableCell>
<TableCell className="text-right">$39,550</TableCell>
</TableRow>
</TableFooter>
</Table>
</Frame>
);
}
Orders
A realistic orders table with customer name, email, order date, and a colored status badge for payment state.
| Order | Customer | Status | Amount |
|---|---|---|---|
| #3210 | OM Olivia MartinFeb 1, 2025 | Paid | $1,999.00 |
| #3209 | JL Jackson LeeJan 28, 2025 | Pending | $39.00 |
| #3208 | IN Isabella NguyenJan 25, 2025 | Paid | $299.00 |
| #3207 | WK William KimJan 22, 2025 | Refunded | $99.00 |
| #3206 | SD Sofia DavisJan 18, 2025 | Paid | $2,500.00 |
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
const orders = [
{
amount: "$1,999.00",
avatar:
"https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=96&h=96&dpr=2&q=80",
customer: "Olivia Martin",
date: "Feb 1, 2025",
email: "olivia@example.com",
id: "#3210",
status: "Paid",
statusVariant: "success" as const,
},
{
amount: "$39.00",
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
customer: "Jackson Lee",
date: "Jan 28, 2025",
email: "jackson@example.com",
id: "#3209",
status: "Pending",
statusVariant: "warning" as const,
},
{
amount: "$299.00",
avatar:
"https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=96&h=96&dpr=2&q=80",
customer: "Isabella Nguyen",
date: "Jan 25, 2025",
email: "isabella@example.com",
id: "#3208",
status: "Paid",
statusVariant: "success" as const,
},
{
amount: "$99.00",
avatar:
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=96&h=96&dpr=2&q=80",
customer: "William Kim",
date: "Jan 22, 2025",
email: "will@example.com",
id: "#3207",
status: "Refunded",
statusVariant: "info" as const,
},
{
amount: "$2,500.00",
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
customer: "Sofia Davis",
date: "Jan 18, 2025",
email: "sofia@example.com",
id: "#3206",
status: "Paid",
statusVariant: "success" as const,
},
];
export function Pattern() {
return (
<div className="mx-auto flex w-full max-w-2xl flex-col">
<Table>
<TableHeader>
<TableRow>
<TableHead>Order</TableHead>
<TableHead>Customer</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{orders.map((order) => (
<TableRow key={order.id}>
<TableCell className="font-mono text-sm">{order.id}</TableCell>
<TableCell>
<div className="flex items-center gap-3">
<Avatar>
<AvatarImage alt={order.customer} src={order.avatar} />
<AvatarFallback>
{order.customer
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<div className="flex flex-col">
<span className="font-medium text-sm">
{order.customer}
</span>
<span className="text-muted-foreground text-xs">
{order.date}
</span>
</div>
</div>
</TableCell>
<TableCell>
<Badge size="sm" variant={order.statusVariant}>
{order.status}
</Badge>
</TableCell>
<TableCell className="text-right font-medium text-sm">
{order.amount}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}
Team Members
Shows team members with avatar initials, full name, role label, and an online/offline status indicator.
| Member | Role | Status |
|---|---|---|
SC Sarah Chensarah@example.com | Admin | Active |
MJ Marcus Johnsonmarcus@example.com | Developer | Active |
EP Emily Parkemily@example.com | Designer | Away |
DK David Kimdavid@example.com | Viewer | Offline |
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Frame, FramePanel } from "@/components/ui/frame";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
const members = [
{
avatar:
"https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=96&h=96&dpr=2&q=80",
email: "sarah@example.com",
name: "Sarah Chen",
role: "Admin",
roleVariant: "default" as const,
status: "Active",
statusVariant: "success" as const,
},
{
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
email: "marcus@example.com",
name: "Marcus Johnson",
role: "Developer",
roleVariant: "info" as const,
status: "Active",
statusVariant: "success" as const,
},
{
avatar:
"https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=96&h=96&dpr=2&q=80",
email: "emily@example.com",
name: "Emily Park",
role: "Designer",
roleVariant: "warning" as const,
status: "Away",
statusVariant: "warning" as const,
},
{
avatar:
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=96&h=96&dpr=2&q=80",
email: "david@example.com",
name: "David Kim",
role: "Viewer",
roleVariant: "outline" as const,
status: "Offline",
statusVariant: "outline" as const,
},
];
export function Pattern() {
return (
<div className="mx-auto flex w-full max-w-2xl flex-col">
<Frame>
<FramePanel className="p-0!">
<Table>
<TableHeader>
<TableRow>
<TableHead>Member</TableHead>
<TableHead>Role</TableHead>
<TableHead>Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{members.map((member) => (
<TableRow key={member.email}>
<TableCell>
<div className="flex items-center gap-3">
<Avatar>
<AvatarImage alt={member.name} src={member.avatar} />
<AvatarFallback>
{member.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<div className="flex flex-col">
<span className="font-medium text-sm">
{member.name}
</span>
<span className="text-muted-foreground text-xs">
{member.email}
</span>
</div>
</div>
</TableCell>
<TableCell>
<Badge size="sm" variant={member.roleVariant}>
{member.role}
</Badge>
</TableCell>
<TableCell>
<Badge size="sm" variant={member.statusVariant}>
{member.status}
</Badge>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</FramePanel>
</Frame>
</div>
);
}
Subscriptions
Lists subscribers with their plan tier badge, billing cycle, renewal date, and monthly amount in a scannable layout.
| Service | Plan | Billing | Status | Actions |
|---|---|---|---|---|
Vercel ProNext: Mar 1, 2025 | Pro | $20/mo | Active | |
GitHub EnterpriseNext: Mar 15, 2025 | Enterprise | $21/user/mo | Active | |
Figma OrganizationNext: Apr 1, 2025 | Organization | $45/editor/mo | Active | |
Slack Business+Next: — | Business+ | $12.50/user/mo | Cancelled | |
Linear StandardNext: Mar 20, 2025 | Standard | $8/user/mo | Trial |
import { SettingsIcon } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
const subscriptions = [
{
billing: "$20/mo",
nextBilling: "Mar 1, 2025",
plan: "Pro",
planVariant: "default" as const,
service: "Vercel Pro",
status: "Active",
statusVariant: "success" as const,
},
{
billing: "$21/user/mo",
nextBilling: "Mar 15, 2025",
plan: "Enterprise",
planVariant: "info" as const,
service: "GitHub Enterprise",
status: "Active",
statusVariant: "success" as const,
},
{
billing: "$45/editor/mo",
nextBilling: "Apr 1, 2025",
plan: "Organization",
planVariant: "warning" as const,
service: "Figma Organization",
status: "Active",
statusVariant: "success" as const,
},
{
billing: "$12.50/user/mo",
nextBilling: "—",
plan: "Business+",
planVariant: "secondary" as const,
service: "Slack Business+",
status: "Cancelled",
statusVariant: "destructive" as const,
},
{
billing: "$8/user/mo",
nextBilling: "Mar 20, 2025",
plan: "Standard",
planVariant: "outline" as const,
service: "Linear Standard",
status: "Trial",
statusVariant: "info" as const,
},
];
export function Pattern() {
return (
<div className="mx-auto flex w-full max-w-2xl flex-col">
<Table>
<TableHeader>
<TableRow>
<TableHead>Service</TableHead>
<TableHead>Plan</TableHead>
<TableHead>Billing</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{subscriptions.map((sub) => (
<TableRow key={sub.service}>
<TableCell>
<div className="flex flex-col">
<span className="font-medium text-sm">{sub.service}</span>
<span className="text-muted-foreground text-xs">
Next: {sub.nextBilling}
</span>
</div>
</TableCell>
<TableCell>
<Badge size="sm" variant={sub.planVariant}>
{sub.plan}
</Badge>
</TableCell>
<TableCell className="text-sm">{sub.billing}</TableCell>
<TableCell>
<Badge size="sm" variant={sub.statusVariant}>
{sub.status}
</Badge>
</TableCell>
<TableCell className="text-right">
<Button className="h-7" size="sm" variant="ghost">
<SettingsIcon aria-hidden="true" className="size-3.5" />
Manage
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}
Sortable API Endpoints
Click any column header to sort ascending or descending. Arrow icons reflect the current sort direction and error rates are contextually colored by severity.
| Method | Endpoint | Avg latency | Req / day | Error % |
|---|---|---|---|---|
| GET | /api/products | 22 ms | 28,900 | 0.0% |
| GET | /api/users | 48 ms | 12,400 | 0.2% |
| POST | /api/auth/login | 210 ms | 8,700 | 3.8% |
| POST | /api/orders | 134 ms | 3,200 | 1.4% |
| PATCH | /api/users/:id | 61 ms | 1,100 | 0.5% |
| DELETE | /api/orders/:id | 38 ms | 540 | 0.0% |
"use client";
import { ArrowDownIcon, ArrowUpDownIcon, ArrowUpIcon } from "lucide-react";
import { useState } from "react";
import { Badge } from "@/components/ui/badge";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
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 function Pattern() {
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>
);
}
Storage Usage
Each row shows a file-type storage bar with a color-coded fill. Members at or above 90% capacity display a "Storage almost full" warning alongside the bar.
| Member | Plan | Usage |
|---|---|---|
OM Olivia Martinolivia@example.com | Pro | 47.2 / 50 GB |
JL Jackson Leejackson@example.com | Free | 3.8 / 5 GB |
IN Isabella Nguyenisabella@example.com | Pro | 18.5 / 50 GB |
WK William Kimwilliam@example.com | Team | 82.1 / 100 GB |
SD Sofia Davissofia@example.com | Free | 4.9 / 5 GB |
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import {
Card,
CardDescription,
CardHeader,
CardPanel,
CardTitle,
} from "@/components/ui/card";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
const members = [
{
avatar:
"https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=96&h=96&dpr=2&q=80",
email: "olivia@example.com",
name: "Olivia Martin",
plan: "Pro",
planVariant: "default" as const,
totalGb: 50,
usedGb: 47.2,
},
{
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
email: "jackson@example.com",
name: "Jackson Lee",
plan: "Free",
planVariant: "outline" as const,
totalGb: 5,
usedGb: 3.8,
},
{
avatar:
"https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=96&h=96&dpr=2&q=80",
email: "isabella@example.com",
name: "Isabella Nguyen",
plan: "Pro",
planVariant: "default" as const,
totalGb: 50,
usedGb: 18.5,
},
{
avatar:
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=96&h=96&dpr=2&q=80",
email: "william@example.com",
name: "William Kim",
plan: "Team",
planVariant: "info" as const,
totalGb: 100,
usedGb: 82.1,
},
{
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
email: "sofia@example.com",
name: "Sofia Davis",
plan: "Free",
planVariant: "outline" as const,
totalGb: 5,
usedGb: 4.9,
},
];
function StorageBar({ used, total }: { used: number; total: number }) {
const pct = Math.min((used / total) * 100, 100);
const color =
pct >= 90 ? "bg-destructive" : pct >= 70 ? "bg-warning" : "bg-primary";
return (
<div className="flex items-center gap-2.5">
<div className="h-1.5 w-24 overflow-hidden rounded-full bg-muted">
<div
className={`h-full rounded-full ${color}`}
style={{ width: `${pct}%` }}
/>
</div>
<span className="text-muted-foreground text-xs tabular-nums">
{used} / {total} GB
</span>
</div>
);
}
export function Pattern() {
const totalUsed = members.reduce((s, m) => s + m.usedGb, 0);
const totalCap = members.reduce((s, m) => s + m.totalGb, 0);
return (
<div className="w-full max-w-2xl">
<Card>
<CardHeader className="border-b">
<CardTitle>Storage Usage</CardTitle>
<CardDescription>
{totalUsed.toFixed(1)} GB of {totalCap} GB used across all members
</CardDescription>
</CardHeader>
<CardPanel className="p-0">
<Table>
<TableHeader>
<TableRow>
<TableHead>Member</TableHead>
<TableHead>Plan</TableHead>
<TableHead>Usage</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{members.map((m) => {
const pct = (m.usedGb / m.totalGb) * 100;
return (
<TableRow key={m.email}>
<TableCell>
<div className="flex items-center gap-3">
<Avatar className="size-8">
<AvatarImage alt={m.name} src={m.avatar} />
<AvatarFallback className="text-xs">
{m.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<div className="flex flex-col">
<span className="font-medium text-sm">{m.name}</span>
<span className="text-muted-foreground text-xs">
{m.email}
</span>
</div>
</div>
</TableCell>
<TableCell>
<Badge size="sm" variant={m.planVariant}>
{m.plan}
</Badge>
</TableCell>
<TableCell>
<div className="flex flex-col gap-1">
<StorageBar total={m.totalGb} used={m.usedGb} />
{pct >= 90 && (
<span className="font-medium text-destructive text-xs">
Storage almost full
</span>
)}
</div>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</CardPanel>
</Card>
</div>
);
}
Audit Log
A security audit log inside a Frame with actor avatar, action badges color-coded by type (create / update / delete / invite), resource, IP address in monospace, and relative timestamps.
Audit Log
6 recent events| Actor | Action | Resource | IP Address | Time |
|---|---|---|---|---|
OM Olivia Martinolivia@example.com | delete | Project · Mobile App | 192.168.1.42 | 2 min ago |
JL Jackson Leejackson@example.com | invite | User · sofia@example.com | 10.0.0.15 | 18 min ago |
IN Isabella Nguyenisabella@example.com | export | Report · Q1 2025 | 203.0.113.7 | 1 hr ago |
WK William Kimwilliam@example.com | update | Settings · Billing | 172.16.0.3 | 3 hr ago |
SD Sofia Davissofia@example.com | login | Auth · Password | 198.51.100.22 | 5 hr ago |
OM Olivia Martinolivia@example.com | create | API Key · Production | 192.168.1.42 | Yesterday |
//biome-ignore-all lint/suspicious/noArrayIndexKey: <>
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Frame, FramePanel } from "@/components/ui/frame";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
type ActionType =
| "login"
| "export"
| "delete"
| "invite"
| "update"
| "create";
const actionVariant: Record<
ActionType,
"success" | "info" | "destructive" | "warning" | "outline"
> = {
create: "success",
delete: "destructive",
export: "outline",
invite: "info",
login: "outline",
update: "warning",
};
const logs = [
{
action: "delete" as ActionType,
actor: {
avatar:
"https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=96&h=96&dpr=2&q=80",
email: "olivia@example.com",
name: "Olivia Martin",
},
ip: "192.168.1.42",
resource: "Project · Mobile App",
time: "2 min ago",
},
{
action: "invite" as ActionType,
actor: {
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
email: "jackson@example.com",
name: "Jackson Lee",
},
ip: "10.0.0.15",
resource: "User · sofia@example.com",
time: "18 min ago",
},
{
action: "export" as ActionType,
actor: {
avatar:
"https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=96&h=96&dpr=2&q=80",
email: "isabella@example.com",
name: "Isabella Nguyen",
},
ip: "203.0.113.7",
resource: "Report · Q1 2025",
time: "1 hr ago",
},
{
action: "update" as ActionType,
actor: {
avatar:
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=96&h=96&dpr=2&q=80",
email: "william@example.com",
name: "William Kim",
},
ip: "172.16.0.3",
resource: "Settings · Billing",
time: "3 hr ago",
},
{
action: "login" as ActionType,
actor: {
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
email: "sofia@example.com",
name: "Sofia Davis",
},
ip: "198.51.100.22",
resource: "Auth · Password",
time: "5 hr ago",
},
{
action: "create" as ActionType,
actor: {
avatar:
"https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=96&h=96&dpr=2&q=80",
email: "olivia@example.com",
name: "Olivia Martin",
},
ip: "192.168.1.42",
resource: "API Key · Production",
time: "Yesterday",
},
];
export function Pattern() {
return (
<div className="mx-auto w-full max-w-3xl">
<div className="mb-3 flex items-center justify-between">
<p className="font-semibold text-sm">Audit Log</p>
<span className="text-muted-foreground text-xs">
{logs.length} recent events
</span>
</div>
<Frame>
<FramePanel className="p-0!">
<Table>
<TableHeader>
<TableRow>
<TableHead>Actor</TableHead>
<TableHead>Action</TableHead>
<TableHead>Resource</TableHead>
<TableHead>IP Address</TableHead>
<TableHead className="text-right">Time</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{logs.map((log, i) => (
<TableRow key={i}>
<TableCell>
<div className="flex items-center gap-2.5">
<Avatar className="size-7">
<AvatarImage
alt={log.actor.name}
src={log.actor.avatar}
/>
<AvatarFallback className="text-xs">
{log.actor.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<div className="flex flex-col">
<span className="font-medium text-xs">
{log.actor.name}
</span>
<span className="text-muted-foreground text-xs">
{log.actor.email}
</span>
</div>
</div>
</TableCell>
<TableCell>
<Badge size="sm" variant={actionVariant[log.action]}>
{log.action}
</Badge>
</TableCell>
<TableCell className="text-muted-foreground text-xs">
{log.resource}
</TableCell>
<TableCell className="font-mono text-muted-foreground text-xs">
{log.ip}
</TableCell>
<TableCell className="text-right text-muted-foreground text-xs">
{log.time}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</FramePanel>
</Frame>
</div>
);
}
Product Inventory
A product inventory table with SKU in monospace, category, price, and a stock-level badge that turns green for In Stock, amber for Low Stock, and red for Out of Stock.
| Product | SKU | Category | Price | Qty | Status |
|---|---|---|---|---|---|
| Wireless Earbuds | SKU-0021 | Electronics | $89.00 | 142 | In Stock |
| Mechanical Keyboard | SKU-0047 | Accessories | $149.00 | 8 | Low Stock |
| USB-C Hub | SKU-0093 | Accessories | $49.00 | 256 | In Stock |
| Laptop Stand | SKU-0104 | Furniture | $79.00 | 0 | Out of Stock |
| Monitor Light Bar | SKU-0118 | Lighting | $59.00 | 14 | Low Stock |
| Webcam 4K | SKU-0132 | Electronics | $199.00 | 67 | In Stock |
import { Badge } from "@/components/ui/badge";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
type StockStatus = "in-stock" | "low-stock" | "out-of-stock";
const stockVariant: Record<StockStatus, "success" | "warning" | "destructive"> =
{
"in-stock": "success",
"low-stock": "warning",
"out-of-stock": "destructive",
};
const stockLabel: Record<StockStatus, string> = {
"in-stock": "In Stock",
"low-stock": "Low Stock",
"out-of-stock": "Out of Stock",
};
const products = [
{
category: "Electronics",
name: "Wireless Earbuds",
price: "$89.00",
qty: 142,
sku: "SKU-0021",
stock: "in-stock" as StockStatus,
},
{
category: "Accessories",
name: "Mechanical Keyboard",
price: "$149.00",
qty: 8,
sku: "SKU-0047",
stock: "low-stock" as StockStatus,
},
{
category: "Accessories",
name: "USB-C Hub",
price: "$49.00",
qty: 256,
sku: "SKU-0093",
stock: "in-stock" as StockStatus,
},
{
category: "Furniture",
name: "Laptop Stand",
price: "$79.00",
qty: 0,
sku: "SKU-0104",
stock: "out-of-stock" as StockStatus,
},
{
category: "Lighting",
name: "Monitor Light Bar",
price: "$59.00",
qty: 14,
sku: "SKU-0118",
stock: "low-stock" as StockStatus,
},
{
category: "Electronics",
name: "Webcam 4K",
price: "$199.00",
qty: 67,
sku: "SKU-0132",
stock: "in-stock" as StockStatus,
},
];
export function Pattern() {
return (
<div className="mx-auto w-full max-w-3xl">
<Table>
<TableHeader>
<TableRow>
<TableHead>Product</TableHead>
<TableHead>SKU</TableHead>
<TableHead>Category</TableHead>
<TableHead className="text-right">Price</TableHead>
<TableHead className="text-right">Qty</TableHead>
<TableHead>Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{products.map((p) => (
<TableRow key={p.sku}>
<TableCell className="font-medium text-sm">{p.name}</TableCell>
<TableCell className="font-mono text-muted-foreground text-xs">
{p.sku}
</TableCell>
<TableCell className="text-muted-foreground text-sm">
{p.category}
</TableCell>
<TableCell className="text-right text-sm">{p.price}</TableCell>
<TableCell className="text-right text-sm tabular-nums">
{p.qty}
</TableCell>
<TableCell>
<Badge size="sm" variant={stockVariant[p.stock]}>
{stockLabel[p.stock]}
</Badge>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}
Invoice History
Lists client invoices with issued and due dates, a colored status badge (Paid, Pending, Overdue, Draft), and a footer row showing the total outstanding balance.
| Invoice | Client | Issued | Due | Status | Amount | |
|---|---|---|---|---|---|---|
| INV-2025-001 | Acme Corp | Jan 5, 2025 | Feb 5, 2025 | Paid | $4,800.00 | |
| INV-2025-002 | Globex Inc | Jan 12, 2025 | Feb 12, 2025 | Overdue | $2,150.00 | |
| INV-2025-003 | Initech Ltd | Jan 20, 2025 | Feb 20, 2025 | Pending | $9,320.00 | |
| INV-2025-004 | Umbrella Co | Jan 27, 2025 | Feb 27, 2025 | Paid | $1,500.00 | |
| INV-2025-005 | Cyberdyne Systems | Feb 3, 2025 | Mar 3, 2025 | Draft | $7,200.00 | |
| Total outstanding | $11,470.00 | |||||
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
const invoices = [
{
amount: "$4,800.00",
client: "Acme Corp",
due: "Feb 5, 2025",
id: "INV-2025-001",
issued: "Jan 5, 2025",
status: "Paid",
statusVariant: "success" as const,
},
{
amount: "$2,150.00",
client: "Globex Inc",
due: "Feb 12, 2025",
id: "INV-2025-002",
issued: "Jan 12, 2025",
status: "Overdue",
statusVariant: "destructive" as const,
},
{
amount: "$9,320.00",
client: "Initech Ltd",
due: "Feb 20, 2025",
id: "INV-2025-003",
issued: "Jan 20, 2025",
status: "Pending",
statusVariant: "warning" as const,
},
{
amount: "$1,500.00",
client: "Umbrella Co",
due: "Feb 27, 2025",
id: "INV-2025-004",
issued: "Jan 27, 2025",
status: "Paid",
statusVariant: "success" as const,
},
{
amount: "$7,200.00",
client: "Cyberdyne Systems",
due: "Mar 3, 2025",
id: "INV-2025-005",
issued: "Feb 3, 2025",
status: "Draft",
statusVariant: "outline" as const,
},
];
export function Pattern() {
return (
<div className="mx-auto w-full max-w-3xl">
<Table>
<TableHeader>
<TableRow>
<TableHead>Invoice</TableHead>
<TableHead>Client</TableHead>
<TableHead>Issued</TableHead>
<TableHead>Due</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Amount</TableHead>
<TableHead />
</TableRow>
</TableHeader>
<TableBody>
{invoices.map((inv) => (
<TableRow key={inv.id}>
<TableCell className="font-medium font-mono text-sm">
{inv.id}
</TableCell>
<TableCell className="text-sm">{inv.client}</TableCell>
<TableCell className="text-muted-foreground text-sm">
{inv.issued}
</TableCell>
<TableCell className="text-muted-foreground text-sm">
{inv.due}
</TableCell>
<TableCell>
<Badge size="sm" variant={inv.statusVariant}>
{inv.status}
</Badge>
</TableCell>
<TableCell className="text-right font-medium text-sm">
{inv.amount}
</TableCell>
<TableCell className="text-right">
<Button className="h-7" size="sm" variant="ghost">
View
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={5}>Total outstanding</TableCell>
<TableCell className="text-right">$11,470.00</TableCell>
<TableCell />
</TableRow>
</TableFooter>
</Table>
</div>
);
}
Feature Comparison
A plan comparison table with features as rows and Free, Pro, and Enterprise as columns. String values show the tier limit; boolean values render a green check or a muted dash.
| Feature | Free | Pro | Enterprise |
|---|---|---|---|
| Projects | |||
| Team members | Up to 3 | Up to 20 | Unlimited |
| Storage | 5 GB | 50 GB | 1 TB |
| Custom domains | |||
| Advanced analytics | |||
| API access | |||
| Priority support | |||
| SSO & SAML | |||
| Audit logs | |||
| Dedicated manager |
import { CheckIcon, MinusIcon } from "lucide-react";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
const features: {
enterprise: boolean | string;
free: boolean | string;
name: string;
pro: boolean | string;
}[] = [
{ enterprise: true, free: true, name: "Projects", pro: true },
{
enterprise: "Unlimited",
free: "Up to 3",
name: "Team members",
pro: "Up to 20",
},
{ enterprise: "1 TB", free: "5 GB", name: "Storage", pro: "50 GB" },
{ enterprise: true, free: false, name: "Custom domains", pro: true },
{ enterprise: true, free: false, name: "Advanced analytics", pro: true },
{ enterprise: true, free: false, name: "API access", pro: true },
{ enterprise: true, free: false, name: "Priority support", pro: true },
{ enterprise: true, free: false, name: "SSO & SAML", pro: false },
{ enterprise: true, free: false, name: "Audit logs", pro: false },
{ enterprise: true, free: false, name: "Dedicated manager", pro: false },
];
function FeatureCell({ value }: { value: boolean | string }) {
if (typeof value === "string") {
return <span className="text-muted-foreground text-sm">{value}</span>;
}
return value ? (
<CheckIcon className="size-4 text-emerald-500" />
) : (
<MinusIcon className="size-4 text-muted-foreground/40" />
);
}
export function Pattern() {
return (
<div className="mx-auto w-full max-w-2xl">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-1/2">Feature</TableHead>
<TableHead className="text-center">Free</TableHead>
<TableHead className="text-center">Pro</TableHead>
<TableHead className="text-center">Enterprise</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{features.map((f) => (
<TableRow key={f.name}>
<TableCell className="font-medium text-sm">{f.name}</TableCell>
<TableCell className="text-center">
<FeatureCell value={f.free} />
</TableCell>
<TableCell className="text-center">
<FeatureCell value={f.pro} />
</TableCell>
<TableCell className="text-center">
<FeatureCell value={f.enterprise} />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}
Server Health
Monitors server instances with region, a status badge, and inline progress bars for CPU and memory utilization alongside uptime percentage in monospace.
| Server | Region | Status | CPU | Memory | Uptime |
|---|---|---|---|---|---|
| web-prod-01 | us-east-1 | Healthy | x | x | 99.98% |
| web-prod-02 | us-east-1 | Healthy | x | x | 99.98% |
| api-prod-01 | us-west-2 | Warning | x | x | 99.81% |
| db-primary | eu-west-1 | Healthy | x | x | 99.99% |
| worker-01 | ap-east-1 | Critical | x | x | 97.23% |
| cache-01 | us-east-1 | Healthy | x | x | 100% |
import { Badge } from "@/components/ui/badge";
import { Progress } from "@/components/ui/progress";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
const servers = [
{
cpu: 42,
memory: 61,
name: "web-prod-01",
region: "us-east-1",
status: "Healthy",
statusVariant: "success" as const,
uptime: "99.98%",
},
{
cpu: 38,
memory: 55,
name: "web-prod-02",
region: "us-east-1",
status: "Healthy",
statusVariant: "success" as const,
uptime: "99.98%",
},
{
cpu: 87,
memory: 79,
name: "api-prod-01",
region: "us-west-2",
status: "Warning",
statusVariant: "warning" as const,
uptime: "99.81%",
},
{
cpu: 23,
memory: 68,
name: "db-primary",
region: "eu-west-1",
status: "Healthy",
statusVariant: "success" as const,
uptime: "99.99%",
},
{
cpu: 96,
memory: 91,
name: "worker-01",
region: "ap-east-1",
status: "Critical",
statusVariant: "destructive" as const,
uptime: "97.23%",
},
{
cpu: 14,
memory: 33,
name: "cache-01",
region: "us-east-1",
status: "Healthy",
statusVariant: "success" as const,
uptime: "100%",
},
];
export function Pattern() {
return (
<div className="mx-auto w-full max-w-3xl">
<Table>
<TableHeader>
<TableRow>
<TableHead>Server</TableHead>
<TableHead>Region</TableHead>
<TableHead>Status</TableHead>
<TableHead>CPU</TableHead>
<TableHead>Memory</TableHead>
<TableHead className="text-right">Uptime</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{servers.map((s) => (
<TableRow key={s.name}>
<TableCell className="font-medium font-mono text-sm">
{s.name}
</TableCell>
<TableCell className="text-muted-foreground text-xs">
{s.region}
</TableCell>
<TableCell>
<Badge size="sm" variant={s.statusVariant}>
{s.status}
</Badge>
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
<Progress className="h-1.5 w-16" value={s.cpu} />
<span className="w-8 text-right text-muted-foreground text-xs tabular-nums">
{s.cpu}%
</span>
</div>
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
<Progress className="h-1.5 w-16" value={s.memory} />
<span className="w-8 text-right text-muted-foreground text-xs tabular-nums">
{s.memory}%
</span>
</div>
</TableCell>
<TableCell className="text-right font-mono text-sm">
{s.uptime}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}
Contributor Leaderboard
A ranked contributor table with avatar, name, commit/PR/review counts, and a colored trend indicator that shows a TrendingUp or TrendingDown icon with the rank change value.
| Rank | Contributor | Commits | PRs | Reviews | Change |
|---|---|---|---|---|---|
| #1 | OMOlivia MartinTop Contributor | 342 | 87 | 156 | +3 |
| #2 | JLJackson Lee | 291 | 64 | 122 | -1 |
| #3 | INIsabella Nguyen | 248 | 71 | 98 | +2 |
| #4 | WKWilliam Kim | 196 | 43 | 87 | — |
| #5 | SDSofia Davis | 163 | 35 | 74 | -2 |
import { TrendingDownIcon, TrendingUpIcon } from "lucide-react";
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
const contributors = [
{
avatar:
"https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=96&h=96&dpr=2&q=80",
badge: "Top Contributor",
change: +3,
commits: 342,
name: "Olivia Martin",
prs: 87,
rank: 1,
reviews: 156,
},
{
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
badge: null,
change: -1,
commits: 291,
name: "Jackson Lee",
prs: 64,
rank: 2,
reviews: 122,
},
{
avatar:
"https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=96&h=96&dpr=2&q=80",
badge: null,
change: +2,
commits: 248,
name: "Isabella Nguyen",
prs: 71,
rank: 3,
reviews: 98,
},
{
avatar:
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=96&h=96&dpr=2&q=80",
badge: null,
change: 0,
commits: 196,
name: "William Kim",
prs: 43,
rank: 4,
reviews: 87,
},
{
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
badge: null,
change: -2,
commits: 163,
name: "Sofia Davis",
prs: 35,
rank: 5,
reviews: 74,
},
];
export function Pattern() {
return (
<div className="mx-auto w-full max-w-2xl">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-12">Rank</TableHead>
<TableHead>Contributor</TableHead>
<TableHead className="text-right">Commits</TableHead>
<TableHead className="text-right">PRs</TableHead>
<TableHead className="text-right">Reviews</TableHead>
<TableHead className="text-right">Change</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{contributors.map((c) => (
<TableRow key={c.rank}>
<TableCell className="font-bold text-muted-foreground text-sm tabular-nums">
#{c.rank}
</TableCell>
<TableCell>
<div className="flex items-center gap-2.5">
<Avatar className="size-7">
<AvatarImage alt={c.name} src={c.avatar} />
<AvatarFallback className="text-xs">
{c.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<span className="font-medium text-sm">{c.name}</span>
{c.badge && (
<Badge size="sm" variant="info">
{c.badge}
</Badge>
)}
</div>
</TableCell>
<TableCell className="text-right text-sm tabular-nums">
{c.commits}
</TableCell>
<TableCell className="text-right text-sm tabular-nums">
{c.prs}
</TableCell>
<TableCell className="text-right text-sm tabular-nums">
{c.reviews}
</TableCell>
<TableCell className="text-right">
<span
className={`inline-flex items-center gap-0.5 text-xs tabular-nums ${
c.change > 0
? "text-emerald-500"
: c.change < 0
? "text-red-500"
: "text-muted-foreground"
}`}
>
{c.change > 0 ? (
<TrendingUpIcon className="size-3" />
) : c.change < 0 ? (
<TrendingDownIcon className="size-3" />
) : null}
{c.change > 0
? `+${c.change}`
: c.change === 0
? "—"
: c.change}
</span>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}

