Creative Tim UICreative Tim UI

Shadcn Table

PreviousNext

Flexible and customizable table components for displaying structured data with sorting, pagination, and styling options.

NameJobEmployed
John MichaelManager23/04/18Edit
Alexa LirasDeveloper23/04/18Edit
Laurent PerrierExecutive19/09/17Edit
Michael LeviDeveloper24/12/08Edit
Richard GranManager04/10/21Edit
const TABLE_HEAD = ["Name", "Job", "Employed", ""]

const TABLE_ROWS = [
  {
    name: "John Michael",
    job: "Manager",
    date: "23/04/18",
  },
  {
    name: "Alexa Liras",
    job: "Developer",
    date: "23/04/18",
  },
  {
    name: "Laurent Perrier",
    job: "Executive",
    date: "19/09/17",
  },
  {
    name: "Michael Levi",
    job: "Developer",
    date: "24/12/08",
  },
  {
    name: "Richard Gran",
    job: "Manager",
    date: "04/10/21",
  },
]

export function TableDemo() {
  return (
    <div className="border-border w-full overflow-hidden rounded-lg border">
      <table className="w-full">
        <thead className="border-border bg-muted border-b text-sm font-medium">
          <tr>
            {TABLE_HEAD.map((head) => (
              <th key={head} className="px-2.5 py-2 text-start font-medium">
                {head}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="text-sm">
          {TABLE_ROWS.map(({ name, job, date }, index) => {
            return (
              <tr key={index} className="border-border border-b last:border-0">
                <td className="p-3">{name}</td>
                <td className="p-3">{job}</td>
                <td className="p-3">{date}</td>
                <td className="p-3">
                  <a
                    href="#"
                    className="text-primary text-sm font-medium hover:underline"
                  >
                    Edit
                  </a>
                </td>
              </tr>
            )
          })}
        </tbody>
      </table>
    </div>
  )
}

Installation

pnpm dlx @creative-tim/ui@latest add table

Usage

// Tables use standard HTML table elements
<table className="w-full">
  <thead>
    <tr>
      <th>Column Header</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Cell Data</td>
    </tr>
  </tbody>
</table>

Examples

Table with Striped Rows

Alternating row colors for improved readability of large datasets.

NameJobEmployed
John MichaelManager23/04/18Edit
Alexa LirasDeveloper23/04/18Edit
Laurent PerrierExecutive19/09/17Edit
Michael LeviDeveloper24/12/08Edit
Richard GranManager04/10/21Edit
const TABLE_HEAD = ["Name", "Job", "Employed", ""]

const TABLE_ROWS = [
  {
    name: "John Michael",
    job: "Manager",
    date: "23/04/18",
  },
  {
    name: "Alexa Liras",
    job: "Developer",
    date: "23/04/18",
  },
  {
    name: "Laurent Perrier",
    job: "Executive",
    date: "19/09/17",
  },
  {
    name: "Michael Levi",
    job: "Developer",
    date: "24/12/08",
  },
  {
    name: "Richard Gran",
    job: "Manager",
    date: "04/10/21",
  },
]

export function TableWithStripedRow() {
  return (
    <div className="border-border w-full overflow-hidden rounded-lg border">
      <table className="w-full">
        <thead className="border-border bg-muted border-b text-sm font-medium">
          <tr>
            {TABLE_HEAD.map((head) => (
              <th key={head} className="px-2.5 py-2 text-start font-medium">
                {head}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="text-sm">
          {TABLE_ROWS.map(({ name, job, date }, index) => {
            return (
              <tr key={index} className="even:bg-muted">
                <td className="p-3">{name}</td>
                <td className="p-3">{job}</td>
                <td className="p-3">{date}</td>
                <td className="p-3">
                  <a
                    href="#"
                    className="text-primary text-sm font-medium hover:underline"
                  >
                    Edit
                  </a>
                </td>
              </tr>
            )
          })}
        </tbody>
      </table>
    </div>
  )
}
<tbody className="text-sm">
  {TABLE_ROWS.map(({ name, job, date }, index) => (
    <tr key={index} className="even:bg-muted">
      <td className="p-3">{name}</td>
      <td className="p-3">{job}</td>
      <td className="p-3">{date}</td>
      <td className="p-3">
        <a
          href="#"
          className="text-primary text-sm font-medium hover:underline"
        >
          Edit
        </a>
      </td>
    </tr>
  ))}
</tbody>

Table with Striped Columns

Alternating column colors to visually separate data columns.

NameJobEmployed
John MichaelManager23/04/18Edit
Alexa LirasDeveloper23/04/18Edit
Laurent PerrierExecutive19/09/17Edit
Michael LeviDeveloper24/12/08Edit
Richard GranManager04/10/21Edit
const TABLE_HEAD = ["Name", "Job", "Employed", ""]

const TABLE_ROWS = [
  {
    name: "John Michael",
    job: "Manager",
    date: "23/04/18",
  },
  {
    name: "Alexa Liras",
    job: "Developer",
    date: "23/04/18",
  },
  {
    name: "Laurent Perrier",
    job: "Executive",
    date: "19/09/17",
  },
  {
    name: "Michael Levi",
    job: "Developer",
    date: "24/12/08",
  },
  {
    name: "Richard Gran",
    job: "Manager",
    date: "04/10/21",
  },
]

export function TableWithStripedColumn() {
  return (
    <div className="border-border w-full overflow-hidden rounded-lg border">
      <table className="w-full">
        <thead className="border-border bg-muted border-b text-sm font-medium">
          <tr>
            {TABLE_HEAD.map((head) => (
              <th key={head} className="px-2.5 py-2 text-start font-medium">
                {head}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="text-sm">
          {TABLE_ROWS.map(({ name, job, date }, index) => {
            const isLast = index === TABLE_ROWS.length - 1
            const classes = isLast ? "p-3" : "p-3 border-b border-border"

            return (
              <tr key={index}>
                <td className={classes}>{name}</td>
                <td className={`${classes} bg-muted`}>{job}</td>
                <td className={classes}>{date}</td>
                <td className={`${classes} bg-muted`}>
                  <a
                    href="#"
                    className="text-primary text-sm font-medium hover:underline"
                  >
                    Edit
                  </a>
                </td>
              </tr>
            )
          })}
        </tbody>
      </table>
    </div>
  )
}
<tbody className="text-sm">
  {TABLE_ROWS.map(({ name, job, date }, index) => {
    const isLast = index === TABLE_ROWS.length - 1
    const classes = isLast ? "p-3" : "p-3 border-b border-border"
 
    return (
      <tr key={index}>
        <td className={classes}>{name}</td>
        <td className={`${classes} bg-muted`}>{job}</td>
        <td className={classes}>{date}</td>
        <td className={`${classes} bg-muted`}>
          <a
            href="#"
            className="text-primary text-sm font-medium hover:underline"
          >
            Edit
          </a>
        </td>
      </tr>
    )
  })}
</tbody>

Table Sortable

Interactive table with sortable columns for data organization.

Members list

See information about all members

Member
Function
Status
Employed
J
John Michael[email protected]
ManagerOrganization
Online23/04/18
A
ProgramatorDeveloper
Offline23/04/18
L
Laurent Perrier[email protected]
ExecutiveProjects
Offline19/09/17
M
Michael Levi[email protected]
ProgramatorDeveloper
Online24/12/08
R
Richard Gran[email protected]
ManagerExecutive
Offline04/10/21
Page 1 of 10
"use client"

import * as React from "react"
import {
  ArrowDown,
  ArrowUp,
  ArrowUpDown,
  Pencil,
  Search,
  UserPlus,
} from "lucide-react"

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip"

const TABS = [
  {
    label: "All",
    value: "all",
  },
  {
    label: "Monitored",
    value: "monitored",
  },
  {
    label: "Unmonitored",
    value: "unmonitored",
  },
]

const TABLE_HEAD = ["Member", "Function", "Status", "Employed", ""]

const TABLE_ROWS = [
  {
    img: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop",
    name: "John Michael",
    email: "[email protected]",
    job: "Manager",
    org: "Organization",
    online: true,
    date: "23/04/18",
  },
  {
    img: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100&h=100&fit=crop",
    name: "Alexa Liras",
    email: "[email protected]",
    job: "Programator",
    org: "Developer",
    online: false,
    date: "23/04/18",
  },
  {
    img: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=100&h=100&fit=crop",
    name: "Laurent Perrier",
    email: "[email protected]",
    job: "Executive",
    org: "Projects",
    online: false,
    date: "19/09/17",
  },
  {
    img: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop",
    name: "Michael Levi",
    email: "[email protected]",
    job: "Programator",
    org: "Developer",
    online: true,
    date: "24/12/08",
  },
  {
    img: "https://images.unsplash.com/photo-1519345182560-3f2917c472ef?w=100&h=100&fit=crop",
    name: "Richard Gran",
    email: "[email protected]",
    job: "Manager",
    org: "Executive",
    online: false,
    date: "04/10/21",
  },
]

type SortKey = "name" | "job" | "status" | "date"
type SortDirection = "asc" | "desc" | null

export function TableSortable() {
  const [sortKey, setSortKey] = React.useState<SortKey | null>(null)
  const [sortDirection, setSortDirection] = React.useState<SortDirection>(null)

  const handleSort = (key: SortKey) => {
    if (sortKey === key) {
      if (sortDirection === "asc") {
        setSortDirection("desc")
      } else if (sortDirection === "desc") {
        setSortKey(null)
        setSortDirection(null)
      } else {
        setSortDirection("asc")
      }
    } else {
      setSortKey(key)
      setSortDirection("asc")
    }
  }

  const sortedRows = React.useMemo(() => {
    if (!sortKey || !sortDirection) return TABLE_ROWS

    return [...TABLE_ROWS].sort((a, b) => {
      let aValue: string | boolean
      let bValue: string | boolean

      if (sortKey === "name") {
        aValue = a.name
        bValue = b.name
      } else if (sortKey === "job") {
        aValue = a.job
        bValue = b.job
      } else if (sortKey === "status") {
        aValue = a.online
        bValue = b.online
      } else {
        aValue = a.date
        bValue = b.date
      }

      if (typeof aValue === "boolean" && typeof bValue === "boolean") {
        return sortDirection === "asc"
          ? (aValue ? 1 : 0) - (bValue ? 1 : 0)
          : (bValue ? 1 : 0) - (aValue ? 1 : 0)
      }

      if (typeof aValue === "string" && typeof bValue === "string") {
        return sortDirection === "asc"
          ? aValue.localeCompare(bValue)
          : bValue.localeCompare(aValue)
      }

      return 0
    })
  }, [sortKey, sortDirection])

  const getSortIcon = (columnKey: SortKey) => {
    if (sortKey !== columnKey) {
      return <ArrowUpDown className="h-4 w-4" />
    }
    if (sortDirection === "asc") {
      return <ArrowUp className="h-4 w-4" />
    }
    return <ArrowDown className="h-4 w-4" />
  }
  return (
    <div className="w-full">
      <div className="mb-8 flex items-center justify-between gap-8">
        <div>
          <h6 className="text-base font-semibold">Members list</h6>
          <p className="text-muted-foreground mt-1 text-sm">
            See information about all members
          </p>
        </div>
        <div className="flex shrink-0 flex-col gap-2 sm:flex-row">
          <Button variant="outline" size="sm">
            View all
          </Button>
          <Button size="sm">
            <UserPlus className="mr-2 h-4 w-4" />
            Add member
          </Button>
        </div>
      </div>
      <div className="flex flex-col items-center justify-between gap-4 md:flex-row">
        <Tabs defaultValue="all" className="w-full md:w-max">
          <TabsList>
            {TABS.map(({ label, value }) => (
              <TabsTrigger key={value} value={value}>
                {label}
              </TabsTrigger>
            ))}
          </TabsList>
        </Tabs>
        <div className="relative w-full md:w-72">
          <Search className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2" />
          <Input placeholder="Search" className="pl-9" />
        </div>
      </div>

      <div className="border-border mt-4 w-full overflow-hidden rounded-lg border">
        <table className="w-full">
          <thead className="border-border bg-muted border-b text-sm font-medium">
            <tr>
              {TABLE_HEAD.map((head, index) => {
                const sortKeys: (SortKey | null)[] = [
                  "name",
                  "job",
                  "status",
                  "date",
                  null,
                ]
                const key = sortKeys[index]

                return (
                  <th
                    key={head}
                    className={`px-2.5 py-2 text-start font-medium ${key ? "hover:bg-muted/80 cursor-pointer" : ""}`}
                    onClick={() => key && handleSort(key)}
                  >
                    <div className="text-muted-foreground flex items-center justify-between gap-2">
                      {head}
                      {key && getSortIcon(key)}
                    </div>
                  </th>
                )
              })}
            </tr>
          </thead>
          <tbody className="text-sm">
            {sortedRows.map(
              ({ img, name, email, job, org, online, date }, index) => {
                return (
                  <tr
                    key={name}
                    className="border-border border-b last:border-0"
                  >
                    <td className="p-3">
                      <div className="flex items-center gap-3">
                        <Avatar className="h-9 w-9">
                          <AvatarImage src={img} alt={name} />
                          <AvatarFallback>{name.charAt(0)}</AvatarFallback>
                        </Avatar>
                        <div className="flex flex-col">
                          <span className="text-sm font-medium">{name}</span>
                          <span className="text-muted-foreground text-xs">
                            {email}
                          </span>
                        </div>
                      </div>
                    </td>
                    <td className="p-3">
                      <div className="flex flex-col">
                        <span className="text-sm font-medium">{job}</span>
                        <span className="text-muted-foreground text-xs">
                          {org}
                        </span>
                      </div>
                    </td>
                    <td className="p-3">
                      <Badge variant={online ? "default" : "secondary"}>
                        {online ? "Online" : "Offline"}
                      </Badge>
                    </td>
                    <td className="p-3">
                      <span className="text-sm">{date}</span>
                    </td>
                    <td className="p-3">
                      <TooltipProvider>
                        <Tooltip>
                          <TooltipTrigger asChild>
                            <Button variant="ghost" size="icon">
                              <Pencil className="h-4 w-4" />
                            </Button>
                          </TooltipTrigger>
                          <TooltipContent>
                            <p>Edit User</p>
                          </TooltipContent>
                        </Tooltip>
                      </TooltipProvider>
                    </td>
                  </tr>
                )
              }
            )}
          </tbody>
        </table>
      </div>
      <div className="border-border flex items-center justify-between border-t py-4">
        <span className="text-muted-foreground text-sm">Page 1 of 10</span>
        <div className="flex gap-2">
          <Button variant="outline" size="sm">
            Previous
          </Button>
          <Button variant="outline" size="sm">
            Next
          </Button>
        </div>
      </div>
    </div>
  )
}
import { ArrowUpDown, Pencil, Search, UserPlus } from "lucide-react"
 
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
 
// Table with sorting indicators
 
;<thead className="border-border bg-muted border-b text-sm font-medium">
  <tr>
    {TABLE_HEAD.map((head, index) => (
      <th
        key={head}
        className="cursor-pointer px-2.5 py-2 text-start font-medium"
      >
        <div className="text-muted-foreground flex items-center justify-between gap-2">
          {head}
          {index !== TABLE_HEAD.length - 1 && (
            <ArrowUpDown className="h-4 w-4" />
          )}
        </div>
      </th>
    ))}
  </tr>
</thead>

Table Members

Complete member management table with avatars, status badges, and actions.

Members list

See information about all members

MemberFunctionStatusEmployed
J
John Michael[email protected]
ManagerOrganization
Online23/04/18
A
ProgramatorDeveloper
Offline23/04/18
L
Laurent Perrier[email protected]
ExecutiveProjects
Offline19/09/17
M
Michael Levi[email protected]
ProgramatorDeveloper
Online24/12/08
R
Richard Gran[email protected]
ManagerExecutive
Offline04/10/21
Page 1 of 10
import { Pencil, Search, UserPlus } from "lucide-react"

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip"

const TABS = [
  {
    label: "All",
    value: "all",
  },
  {
    label: "Monitored",
    value: "monitored",
  },
  {
    label: "Unmonitored",
    value: "unmonitored",
  },
]

const TABLE_HEAD = ["Member", "Function", "Status", "Employed", ""]

const TABLE_ROWS = [
  {
    img: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop",
    name: "John Michael",
    email: "[email protected]",
    job: "Manager",
    org: "Organization",
    online: true,
    date: "23/04/18",
  },
  {
    img: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100&h=100&fit=crop",
    name: "Alexa Liras",
    email: "[email protected]",
    job: "Programator",
    org: "Developer",
    online: false,
    date: "23/04/18",
  },
  {
    img: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=100&h=100&fit=crop",
    name: "Laurent Perrier",
    email: "[email protected]",
    job: "Executive",
    org: "Projects",
    online: false,
    date: "19/09/17",
  },
  {
    img: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop",
    name: "Michael Levi",
    email: "[email protected]",
    job: "Programator",
    org: "Developer",
    online: true,
    date: "24/12/08",
  },
  {
    img: "https://images.unsplash.com/photo-1519345182560-3f2917c472ef?w=100&h=100&fit=crop",
    name: "Richard Gran",
    email: "[email protected]",
    job: "Manager",
    org: "Executive",
    online: false,
    date: "04/10/21",
  },
]

export function TableMembers() {
  return (
    <div className="w-full">
      <div className="mb-8 flex items-center justify-between gap-8">
        <div>
          <h6 className="text-base font-semibold">Members list</h6>
          <p className="text-muted-foreground mt-1 text-sm">
            See information about all members
          </p>
        </div>
        <div className="flex shrink-0 flex-col gap-2 sm:flex-row">
          <Button variant="outline" size="sm">
            View all
          </Button>
          <Button size="sm">
            <UserPlus className="mr-2 h-4 w-4" />
            Add member
          </Button>
        </div>
      </div>
      <div className="flex flex-col items-center justify-between gap-4 md:flex-row">
        <Tabs defaultValue="all" className="w-full md:w-max">
          <TabsList>
            {TABS.map(({ label, value }) => (
              <TabsTrigger key={value} value={value}>
                {label}
              </TabsTrigger>
            ))}
          </TabsList>
        </Tabs>
        <div className="relative w-full md:w-72">
          <Search className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2" />
          <Input placeholder="Search" className="pl-9" />
        </div>
      </div>

      <div className="border-border mt-4 w-full overflow-hidden rounded-lg border">
        <table className="w-full">
          <thead className="border-border bg-muted border-b text-sm font-medium">
            <tr>
              {TABLE_HEAD.map((head) => (
                <th key={head} className="px-2.5 py-2 text-start font-medium">
                  {head}
                </th>
              ))}
            </tr>
          </thead>
          <tbody className="text-sm">
            {TABLE_ROWS.map(
              ({ img, name, email, job, org, online, date }, index) => {
                return (
                  <tr
                    key={name}
                    className="border-border border-b last:border-0"
                  >
                    <td className="p-3">
                      <div className="flex items-center gap-3">
                        <Avatar className="h-9 w-9">
                          <AvatarImage src={img} alt={name} />
                          <AvatarFallback>{name.charAt(0)}</AvatarFallback>
                        </Avatar>
                        <div className="flex flex-col">
                          <span className="text-sm font-medium">{name}</span>
                          <span className="text-muted-foreground text-xs">
                            {email}
                          </span>
                        </div>
                      </div>
                    </td>
                    <td className="p-3">
                      <div className="flex flex-col">
                        <span className="text-sm font-medium">{job}</span>
                        <span className="text-muted-foreground text-xs">
                          {org}
                        </span>
                      </div>
                    </td>
                    <td className="p-3">
                      <Badge variant={online ? "default" : "secondary"}>
                        {online ? "Online" : "Offline"}
                      </Badge>
                    </td>
                    <td className="p-3">
                      <span className="text-sm">{date}</span>
                    </td>
                    <td className="p-3">
                      <TooltipProvider>
                        <Tooltip>
                          <TooltipTrigger asChild>
                            <Button variant="ghost" size="icon">
                              <Pencil className="h-4 w-4" />
                            </Button>
                          </TooltipTrigger>
                          <TooltipContent>
                            <p>Edit User</p>
                          </TooltipContent>
                        </Tooltip>
                      </TooltipProvider>
                    </td>
                  </tr>
                )
              }
            )}
          </tbody>
        </table>
      </div>
      <div className="border-border flex items-center justify-between border-t py-4">
        <span className="text-muted-foreground text-sm">Page 1 of 10</span>
        <div className="flex gap-2">
          <Button variant="outline" size="sm">
            Previous
          </Button>
          <Button variant="outline" size="sm">
            Next
          </Button>
        </div>
      </div>
    </div>
  )
}
import { Pencil, UserPlus, Search } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
 
// Member row with avatar and status
<td className="p-3">
  <div className="flex items-center gap-3">
    <Avatar className="h-9 w-9">
      <AvatarImage src={img} alt={name} />
      <AvatarFallback>{name.charAt(0)}</AvatarFallback>
    </Avatar>
    <div className="flex flex-col">
      <span className="text-sm font-medium">{name}</span>
      <span className="text-xs text-muted-foreground">{email}</span>
    </div>
  </div>
</td>
<td className="p-3">
  <Badge variant={online ? "default" : "secondary"}>
    {online ? "Online" : "Offline"}
  </Badge>
</td>

Table Transactions

Detailed transaction table with logos, status badges, and pagination.

Recent Transactions

These are details about the last transactions

TransactionAmountDateStatusAccount
SSpotify
$2,500Wed 3:00pmpaid
V
visa 123406/2026
AAmazon
$5,000Wed 1:00pmpaid
M
master card 123406/2026
PPinterest
$3,400Mon 7:40pmpending
M
master card 123406/2026
GGoogle
$1,000Wed 5:00pmpaid
V
visa 123406/2026
nnetflix
$14,000Wed 3:30amcancelled
V
visa 123406/2026
import { Download, Pencil, Search } from "lucide-react"

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip"

const TABLE_HEAD = ["Transaction", "Amount", "Date", "Status", "Account", ""]

const TABLE_ROWS = [
  {
    img: "https://docs.material-tailwind.com/img/logos/logo-spotify.svg",
    name: "Spotify",
    amount: "$2,500",
    date: "Wed 3:00pm",
    status: "paid",
    account: "visa",
    accountNumber: "1234",
    expiry: "06/2026",
  },
  {
    img: "https://docs.material-tailwind.com/img/logos/logo-amazon.svg",
    name: "Amazon",
    amount: "$5,000",
    date: "Wed 1:00pm",
    status: "paid",
    account: "master-card",
    accountNumber: "1234",
    expiry: "06/2026",
  },
  {
    img: "https://docs.material-tailwind.com/img/logos/logo-pinterest.svg",
    name: "Pinterest",
    amount: "$3,400",
    date: "Mon 7:40pm",
    status: "pending",
    account: "master-card",
    accountNumber: "1234",
    expiry: "06/2026",
  },
  {
    img: "https://docs.material-tailwind.com/img/logos/logo-google.svg",
    name: "Google",
    amount: "$1,000",
    date: "Wed 5:00pm",
    status: "paid",
    account: "visa",
    accountNumber: "1234",
    expiry: "06/2026",
  },
  {
    img: "https://docs.material-tailwind.com/img/logos/logo-netflix.svg",
    name: "netflix",
    amount: "$14,000",
    date: "Wed 3:30am",
    status: "cancelled",
    account: "visa",
    accountNumber: "1234",
    expiry: "06/2026",
  },
]

export function TableTransactions() {
  return (
    <div className="w-full px-2">
      <div className="mb-4 flex flex-col justify-between gap-8 md:flex-row md:items-center">
        <div>
          <h6 className="text-base font-semibold">Recent Transactions</h6>
          <p className="text-muted-foreground mt-1 text-sm">
            These are details about the last transactions
          </p>
        </div>
        <div className="flex w-full shrink-0 gap-2 md:w-max">
          <div className="relative w-full md:w-72">
            <Search className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2" />
            <Input placeholder="Search" className="pl-9" />
          </div>
          <Button size="sm">
            <Download className="mr-2 h-4 w-4" />
            Download
          </Button>
        </div>
      </div>

      <div className="border-border w-full overflow-hidden rounded-lg border">
        <table className="w-full text-left">
          <thead className="border-border bg-muted border-b text-sm font-medium">
            <tr>
              {TABLE_HEAD.map((head) => (
                <th key={head} className="px-2.5 py-2 text-start font-medium">
                  {head}
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            {TABLE_ROWS.map(
              (
                {
                  img,
                  name,
                  amount,
                  date,
                  status,
                  account,
                  accountNumber,
                  expiry,
                },
                index
              ) => {
                const isLast = index === TABLE_ROWS.length - 1
                const classes = isLast ? "p-4" : "p-4 border-b border-border"

                return (
                  <tr key={name}>
                    <td className={classes}>
                      <div className="flex items-center gap-3">
                        <Avatar className="border-border bg-muted h-10 w-10 rounded-md border p-1">
                          <AvatarImage
                            src={img}
                            alt={name}
                            className="object-contain"
                          />
                          <AvatarFallback>{name.charAt(0)}</AvatarFallback>
                        </Avatar>
                        <span className="text-sm font-bold">{name}</span>
                      </div>
                    </td>
                    <td className={classes}>
                      <span className="text-sm">{amount}</span>
                    </td>
                    <td className={classes}>
                      <span className="text-sm">{date}</span>
                    </td>
                    <td className={classes}>
                      <Badge
                        variant={
                          status === "paid"
                            ? "default"
                            : status === "pending"
                              ? "secondary"
                              : "destructive"
                        }
                      >
                        {status}
                      </Badge>
                    </td>
                    <td className={classes}>
                      <div className="flex items-center gap-3">
                        <div className="border-border h-9 w-12 rounded-md border p-1">
                          <Avatar className="h-full w-full rounded-none">
                            <AvatarImage
                              src={
                                account === "visa"
                                  ? "https://demos.creative-tim.com/test/corporate-ui-dashboard/assets/img/logos/visa.png"
                                  : "https://demos.creative-tim.com/test/corporate-ui-dashboard/assets/img/logos/mastercard.png"
                              }
                              alt={account}
                              className="h-full w-full object-contain p-1"
                            />
                            <AvatarFallback className="rounded-none">
                              {account.charAt(0).toUpperCase()}
                            </AvatarFallback>
                          </Avatar>
                        </div>
                        <div className="flex flex-col">
                          <span className="text-sm capitalize">
                            {account.split("-").join(" ")} {accountNumber}
                          </span>
                          <span className="text-muted-foreground text-xs">
                            {expiry}
                          </span>
                        </div>
                      </div>
                    </td>
                    <td className={classes}>
                      <TooltipProvider>
                        <Tooltip>
                          <TooltipTrigger asChild>
                            <Button variant="ghost" size="icon">
                              <Pencil className="h-4 w-4" />
                            </Button>
                          </TooltipTrigger>
                          <TooltipContent>
                            <p>Edit User</p>
                          </TooltipContent>
                        </Tooltip>
                      </TooltipProvider>
                    </td>
                  </tr>
                )
              }
            )}
          </tbody>
        </table>
      </div>
      <div className="border-border flex items-center justify-between border-t py-4">
        <Button variant="outline" size="sm">
          Previous
        </Button>
        <div className="flex items-center gap-2">
          <Button size="sm" variant="default">
            1
          </Button>
          <Button variant="ghost" size="sm">
            2
          </Button>
          <Button variant="ghost" size="sm">
            3
          </Button>
          <Button variant="ghost" size="sm">
            ...
          </Button>
          <Button variant="ghost" size="sm">
            8
          </Button>
          <Button variant="ghost" size="sm">
            9
          </Button>
          <Button variant="ghost" size="sm">
            10
          </Button>
        </div>
        <Button variant="outline" size="sm">
          Next
        </Button>
      </div>
    </div>
  )
}
import { Download, Search, Pencil } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
 
// Transaction row with company logo and payment method
<td className={classes}>
  <div className="flex items-center gap-3">
    <Avatar className="h-10 w-10 rounded-md border border-border bg-muted p-1">
      <AvatarImage src={img} alt={name} className="object-contain" />
      <AvatarFallback>{name.charAt(0)}</AvatarFallback>
    </Avatar>
    <span className="text-sm font-bold">{name}</span>
  </div>
</td>
<td className={classes}>
  <Badge variant={
    status === "paid" ? "default" : status === "pending" ? "secondary" : "destructive"
  }>
    {status}
  </Badge>
</td>

Table Structure

Tables are built using standard HTML table elements with Tailwind CSS classes for styling:

Table Container

  • overflow-hidden rounded-lg border border-border - Container styling with rounded corners and border
  • w-full - Full width table

Table Header

  • thead - Table header section
  • border-b border-border bg-muted - Header styling with bottom border and muted background
  • text-sm font-medium - Typography for header cells

Table Body

  • tbody - Table body section
  • text-sm - Typography for body cells
  • border-b border-border last:border-0 - Row borders (no border on last row)

Table Cells

  • th - Header cells with text-start for left alignment
  • td - Data cells with p-3 for padding
  • Use className for conditional styling

Props and Customization

Table Variants

Basic Table

  • Simple table structure with borders and headers
  • Use for straightforward data display

Striped Rows

  • Add even:bg-muted to <tr> elements
  • Improves readability for long lists

Striped Columns

  • Add bg-muted to specific <td> elements
  • Visually separates columns

Interactive Tables

  • Combine with Button, Input, Badge, Avatar components
  • Add sorting, filtering, and actions
  • Include tooltips for better UX

Styling

Tables support all standard Tailwind CSS classes:

  • Border: border, border-border, border-b
  • Background: bg-muted, bg-card, custom colors
  • Spacing: p-3, px-2.5 py-2, gap-3
  • Typography: text-sm, text-xs, font-medium, font-bold
  • States: hover:, even:, last:, cursor-pointer

Responsive Design

Make tables responsive with horizontal scrolling:

<div className="w-full overflow-x-auto">
  <table className="min-w-full">{/* table content */}</table>
</div>

For mobile-friendly tables, consider using card layouts or hiding less important columns.