Table
WidgetsReactTailwindData table with sortable columns, filtering, pagination, row selection, and empty/loading states.
Default
| Name | Role | Status | |
|---|---|---|---|
| Erik Yuntantyo | erik@example.com | Admin | Active |
| Jane Smith | jane@example.com | Editor | Active |
| Mike Johnson | mike@example.com | Viewer | Inactive |
| Sarah Wilson | sarah@example.com | Editor | Active |
| Tom Brown | tom@example.com | Viewer | Pending |
With Sorting
| Status | |||
|---|---|---|---|
| Erik Yuntantyo | erik@example.com | Admin | Active |
| Jane Smith | jane@example.com | Editor | Active |
| Mike Johnson | mike@example.com | Viewer | Inactive |
With Pagination
| Name | Role | Status | |
|---|---|---|---|
| Erik Yuntantyo | erik@example.com | Admin | Active |
| Jane Smith | jane@example.com | Editor | Active |
| Mike Johnson | mike@example.com | Viewer | Inactive |
Showing 1–3 of 12 results
Mobile Card Layout
| Name | Role | Status | |
|---|---|---|---|
| Erik Yuntantyo | erik@example.com | Admin | Active |
| Jane Smith | jane@example.com | Editor | Active |
| Mike Johnson | mike@example.com | Viewer | Inactive |
EY
Erik Yuntantyo
erik@example.com
Role
Admin
Status
Active
JS
Jane Smith
jane@example.com
Role
Editor
Status
Active
MJ
Mike Johnson
mike@example.com
Role
Viewer
Status
Inactive
Row Selection
| Name | Role | Status | ||
|---|---|---|---|---|
| Erik Yuntantyo | erik@example.com | Admin | Active | |
| Jane Smith | jane@example.com | Editor | Active | |
| Mike Johnson | mike@example.com | Viewer | Inactive | |
| Sarah Wilson | sarah@example.com | Editor | Active | |
| Tom Brown | tom@example.com | Viewer | Pending |
2 of 5 row(s) selected
Empty & Loading States
| Name | Status | |
|---|---|---|
No results found. | ||
| Name | Status | |
|---|---|---|
1 "use client"; 2 3 import { 4 Table, TableHeader, TableBody, TableHead, TableRow, TableCell, 5 } from "@/shared/ui/table"; 6 import { Badge } from "@/shared/ui/badge"; 7 import { Button } from "@/shared/ui/button"; 8 import { Avatar, AvatarFallback } from "@/shared/ui/avatar"; 9 import { Skeleton } from "@/shared/ui/skeleton"; 10 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 11 import { faChevronUp, faChevronDown, faChevronLeft, faChevronRight, faTrash } from "@fortawesome/free-solid-svg-icons"; 12 13 // Sample data 14 const users = [ 15 { name: "Erik Yuntantyo", email: "erik@example.com", role: "Admin", status: "Active" }, 16 { name: "Jane Smith", email: "jane@example.com", role: "Editor", status: "Active" }, 17 { name: "Mike Johnson", email: "mike@example.com", role: "Viewer", status: "Inactive" }, 18 { name: "Sarah Wilson", email: "sarah@example.com", role: "Editor", status: "Active" }, 19 { name: "Tom Brown", email: "tom@example.com", role: "Viewer", status: "Pending" }, 20 ]; 21 22 // Status badge — 3 variants 23 function StatusBadge({ status }) { 24 const variant = status === "Active" ? "default" : status === "Pending" ? "secondary" : "outline"; 25 return <Badge variant={variant}>{status}</Badge>; 26 } 27 28 // Default table 29 <Table> 30 <TableHeader> 31 <TableRow> 32 <TableHead>Name</TableHead> 33 <TableHead>Email</TableHead> 34 <TableHead>Role</TableHead> 35 <TableHead>Status</TableHead> 36 </TableRow> 37 </TableHeader> 38 <TableBody> 39 {users.map((user) => ( 40 <TableRow key={user.email}> 41 <TableCell className="font-medium">{user.name}</TableCell> 42 <TableCell className="text-muted-foreground">{user.email}</TableCell> 43 <TableCell>{user.role}</TableCell> 44 <TableCell><StatusBadge status={user.status} /></TableCell> 45 </TableRow> 46 ))} 47 </TableBody> 48 </Table> 49 50 // With sorting — sortable column headers with chevron icons 51 <TableHead> 52 <button className="flex items-center gap-1"> 53 Name 54 <FontAwesomeIcon icon={faChevronUp} className="text-primary h-2.5 w-2.5" /> 55 </button> 56 </TableHead> 57 // Sort and slice rows 58 {[...users] 59 .sort((a, b) => a.name.localeCompare(b.name)) 60 .slice(0, 3) 61 .map((user) => ( 62 <TableRow key={user.email}>{/* cells */}</TableRow> 63 ))} 64 65 // With pagination 66 <div className="flex items-center justify-between border-t px-4 py-3"> 67 <p className="text-muted-foreground text-sm">Showing 1–3 of 12 results</p> 68 <div className="flex items-center gap-1"> 69 <Button variant="outline" size="sm" disabled> 70 <FontAwesomeIcon icon={faChevronLeft} className="h-3 w-3" /> 71 </Button> 72 <Button variant="outline" size="sm" className="bg-primary text-primary-foreground">1</Button> 73 <Button variant="outline" size="sm">2</Button> 74 <Button variant="outline" size="sm">3</Button> 75 <Button variant="outline" size="sm">4</Button> 76 <Button variant="outline" size="sm"> 77 <FontAwesomeIcon icon={faChevronRight} className="h-3 w-3" /> 78 </Button> 79 </div> 80 </div> 81 82 // Mobile card layout — hidden on desktop, shown on mobile 83 <div className="hidden md:block"> 84 <Table>{/* ... desktop table ... */}</Table> 85 </div> 86 <div className="space-y-3 md:hidden"> 87 {users.slice(0, 3).map((user) => ( 88 <div key={user.email} className="space-y-3 rounded-lg border p-4"> 89 <div className="flex items-center gap-3"> 90 <Avatar className="h-10 w-10"> 91 <AvatarFallback>{user.name.split(" ").map((n) => n[0]).join("")}</AvatarFallback> 92 </Avatar> 93 <div> 94 <p className="font-medium">{user.name}</p> 95 <p className="text-muted-foreground text-sm">{user.email}</p> 96 </div> 97 </div> 98 <div className="grid grid-cols-2 gap-2 text-sm"> 99 <div> 100 <span className="text-muted-foreground">Role</span> 101 <p className="mt-1">{user.role}</p> 102 </div> 103 <div> 104 <span className="text-muted-foreground">Status</span> 105 <div className="mt-1"><StatusBadge status={user.status} /></div> 106 </div> 107 </div> 108 </div> 109 ))} 110 </div> 111 112 // Row selection — checkbox column + data-state for selected rows 113 <Table> 114 <TableHeader> 115 <TableRow> 116 <TableHead className="w-12"> 117 <input type="checkbox" onChange={toggleAll} /> 118 </TableHead> 119 <TableHead>Name</TableHead> 120 <TableHead>Email</TableHead> 121 <TableHead>Role</TableHead> 122 <TableHead>Status</TableHead> 123 </TableRow> 124 </TableHeader> 125 <TableBody> 126 {users.map((user, i) => ( 127 <TableRow key={user.email} data-state={i < 2 ? "selected" : undefined}> 128 <TableCell> 129 <input type="checkbox" checked={i < 2} onChange={() => toggle(user.email)} /> 130 </TableCell> 131 <TableCell className="font-medium">{user.name}</TableCell> 132 <TableCell className="text-muted-foreground">{user.email}</TableCell> 133 <TableCell>{user.role}</TableCell> 134 <TableCell><StatusBadge status={user.status} /></TableCell> 135 </TableRow> 136 ))} 137 </TableBody> 138 </Table> 139 <div className="flex items-center justify-between border-t px-4 py-3"> 140 <p className="text-muted-foreground text-sm">2 of 5 row(s) selected</p> 141 <Button variant="destructive" size="sm"> 142 <FontAwesomeIcon icon={faTrash} className="mr-1.5 h-3 w-3" /> 143 Delete 144 </Button> 145 </div> 146 147 // Empty state 148 <TableBody> 149 <TableRow> 150 <TableCell colSpan={3} className="h-24 text-center"> 151 No results found. 152 </TableCell> 153 </TableRow> 154 </TableBody> 155 156 // Loading state with Skeleton 157 <TableBody> 158 {Array.from({ length: 3 }).map((_, i) => ( 159 <TableRow key={i}> 160 <TableCell><Skeleton className="h-4 w-24" /></TableCell> 161 <TableCell><Skeleton className="h-4 w-32" /></TableCell> 162 <TableCell><Skeleton className="h-4 w-16" /></TableCell> 163 </TableRow> 164 ))} 165 </TableBody>