New

DataTable

Advanced table with sortable headers, alternating rows, and status badges

Data Displaytablesortabledatagrid

Dependencies

shadcn/ui components needed:

npx shadcn@latest add cardnpx shadcn@latest add badgenpx shadcn@latest add table

How to use this component

Copy the code below into your project. Make sure you have the required shadcn/ui dependencies installed. Then import and use the component in your pages or layouts.

Code

1"use client"
2
3import * as React from "react"
4import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
5import { Badge } from "@/components/ui/badge"
6import {
7 Table,
8 TableBody,
9 TableCell,
10 TableHead,
11 TableHeader,
12 TableRow,
13} from "@/components/ui/table"
14import { cn } from "@/lib/utils"
15
16interface DataTableProps {
17 className?: string
18}
19
20type SortDirection = "asc" | "desc" | null
21
22interface Column {
23 key: string
24 label: string
25 sortable: boolean
26}
27
28interface Row {
29 id: string
30 name: string
31 status: "active" | "inactive" | "pending"
32 role: string
33 email: string
34 lastActive: string
35}
36
37const statusVariants = {
38 active: "bg-emerald-500/10 text-emerald-600 border-emerald-200 hover:bg-emerald-500/20",
39 inactive: "bg-red-500/10 text-red-600 border-red-200 hover:bg-red-500/20",
40 pending: "bg-amber-500/10 text-amber-600 border-amber-200 hover:bg-amber-500/20",
41}
42
43export function DataTable({ className }: DataTableProps) {
44 const [sortColumn, setSortColumn] = React.useState<string | null>(null)
45 const [sortDirection, setSortDirection] = React.useState<SortDirection>(null)
46
47 const columns: Column[] = [
48 { key: "name", label: "Name", sortable: true },
49 { key: "status", label: "Status", sortable: true },
50 { key: "role", label: "Role", sortable: true },
51 { key: "email", label: "Email", sortable: true },
52 { key: "lastActive", label: "Last Active", sortable: true },
53 ]
54
55 const [rows, setRows] = React.useState<Row[]>([
56 { id: "1", name: "Sarah Chen", status: "active", role: "Designer", email: "sarah@example.com", lastActive: "2 min ago" },
57 { id: "2", name: "Alex Rivera", status: "active", role: "Developer", email: "alex@example.com", lastActive: "15 min ago" },
58 { id: "3", name: "Jordan Lee", status: "pending", role: "Manager", email: "jordan@example.com", lastActive: "1 hour ago" },
59 { id: "4", name: "Morgan Smith", status: "inactive", role: "Analyst", email: "morgan@example.com", lastActive: "3 days ago" },
60 { id: "5", name: "Taylor Kim", status: "active", role: "Engineer", email: "taylor@example.com", lastActive: "5 min ago" },
61 { id: "6", name: "Casey Brown", status: "pending", role: "Designer", email: "casey@example.com", lastActive: "2 hours ago" },
62 ])
63
64 const handleSort = (key: string) => {
65 if (sortColumn === key) {
66 if (sortDirection === "asc") {
67 setSortDirection("desc")
68 } else if (sortDirection === "desc") {
69 setSortColumn(null)
70 setSortDirection(null)
71 } else {
72 setSortDirection("asc")
73 }
74 } else {
75 setSortColumn(key)
76 setSortDirection("asc")
77 }
78
79 if (sortColumn === key && sortDirection === null) {
80 setRows(rows.slice().reverse())
81 } else {
82 const sorted = [...rows].sort((a, b) => {
83 const aVal = a[key as keyof Row]
84 const bVal = b[key as keyof Row]
85 if (sortDirection === "asc") {
86 return aVal > bVal ? 1 : -1
87 }
88 return aVal < bVal ? 1 : -1
89 })
90 setRows(sorted)
91 }
92 }
93
94 return (
95 <Card className={cn("w-full", className)}>
96 <CardHeader>
97 <CardTitle>Team Members</CardTitle>
98 </CardHeader>
99 <CardContent>
100 <Table>
101 <TableHeader>
102 <TableRow>
103 {columns.map((col) => (
104 <TableHead key={col.key}>
105 {col.sortable ? (
106 <button
107 onClick={() => handleSort(col.key)}
108 className="flex items-center gap-1 hover:text-primary transition-colors font-medium"
109 >
110 {col.label}
111 {sortColumn === col.key && (
112 <span className="text-xs">
113 {sortDirection === "asc" ? "▲" : sortDirection === "desc" ? "▼" : ""}
114 </span>
115 )}
116 </button>
117 ) : (
118 col.label
119 )}
120 </TableHead>
121 ))}
122 </TableRow>
123 </TableHeader>
124 <TableBody>
125 {rows.map((row, i) => (
126 <TableRow key={row.id} className={cn("hover:bg-muted/50 transition-colors", i % 2 === 0 && "bg-muted/20")}>
127 <TableCell className="font-medium">{row.name}</TableCell>
128 <TableCell>
129 <Badge variant="outline" className={cn("text-xs", statusVariants[row.status])}>
130 {row.status}
131 </Badge>
132 </TableCell>
133 <TableCell>{row.role}</TableCell>
134 <TableCell className="text-muted-foreground">{row.email}</TableCell>
135 <TableCell className="text-muted-foreground">{row.lastActive}</TableCell>
136 </TableRow>
137 ))}
138 </TableBody>
139 </Table>
140 </CardContent>
141 </Card>
142 )
143}

Related Data Display Components

Command Palette

Search for a command to run...