The table displays data from any record source
Name | Status | Role | Location | Time | Start Date | Bio | Actions | |
---|---|---|---|---|---|---|---|---|
![]() Sarah Johnson @sarah | Online | Software Engineer | sarah.johnson@skywardui.com | San Francisco, CA | 21:57GMT-7 | May 10, 2022 | Passionate about coding and building innovative solutions. Enjoys hiking and exploring new technologies. | |
![]() Michael Rodriguez @michael | Offline | Marketing Specialist | michael.rodriguez@skywardui.com | London, UK | 21:57GMT+0 | Sep 22, 2021 | Creative marketer with a knack for social media. Enjoys photography and exploring local coffee shops. | |
![]() Alex Chen @alex | Busy | Data Analyst | alex.chen@skywardui.com | Seattle, WA | 21:57GMT-7 | Jan 8, 2023 | Analytical thinker with a passion for interpreting data. Enjoys playing chess and attending tech meetups. | |
![]() Olivia Thompson @olivia | Online | HR Coordinator | olivia.thompson@skywardui.com | Chicago, IL | 21:57GMT-6 | Nov 14, 2020 | People-oriented professional with a focus on creating a positive workplace. Enjoys reading and yoga. | |
![]() Ryan Patel @ryan | Online | Sales Representative | ryan.patel@skywardui.com | Miami, FL | 21:57GMT-5 | Mar 5, 2022 | Results-driven salesperson with a passion for building client relationships. Enjoys water sports and traveling. | |
![]() Emily Davis @emily | Away | UX/UI Designer | emily.davis@skywardui.com | Los Angeles, CA | 21:57GMT-7 | Jul 18, 2021 | Creative designer with an eye for user experience. Enjoys painting and attending design conferences. | |
![]() Kevin Lewis @kevin | Offline | Financial Analyst | kevin.lewis@skywardui.com | Houston, TX | 21:57GMT-6 | Sep 30, 2022 | Detail-oriented finance professional with a passion for number crunching. Enjoys playing guitar and hiking. | |
![]() Jessica Kim @jessica | Online | Customer Support Specialist | jessica.kim@skywardui.com | Denver, CO | 21:57GMT-6 | Apr 12, 2023 | Customer-focused professional with excellent problem-solving skills. Enjoys gardening and volunteering. | |
![]() Brian Foster @brian | Away | Project Manager | brian.foster@skywardui.com | Atlanta, GA | 21:57GMT-5 | Jan 25, 2021 | Organized project manager with a focus on delivering results. Enjoys hiking and playing golf. | |
![]() Amanda Ramirez @amanda | Online | Content Writer | amanda.ramirez@skywardui.com | Austin, TX | 21:57GMT-6 | Jun 7, 2022 | Creative writer with a passion for storytelling. Enjoys attending book clubs and exploring local breweries. |
In order to make tables easier to use, we’ve created some components with pre-defined styles to allow easier implementation. You can add to or change these styles as you need.
Some components like thead
and tbody
do not require additional styling for our use case, so we’ve left them as the default react components but they are still required in the table structure.
You can add any component you’d like within the TableCell
or TableHeaderCell
components to give you the functionality you need, as shown in the other examples.
import { Table, TableCell, TableHeaderCell, TableRow } from "#/ui/components/Table"
Column 1 | Column 2 | Column 3 |
---|---|---|
Cell 1 | Cell 2 | Cell 3 |
Cell 4 | Cell 5 | Cell 6 |
Cell 7 | Cell 8 | Cell 9 |
<Table>
<thead>
<TableRow>
<TableHeaderCell>Column 1</TableHeaderCell>
<TableHeaderCell>Column 2</TableHeaderCell>
<TableHeaderCell>Column 3</TableHeaderCell>
</TableRow>
</thead>
<tbody>
<TableRow>
<TableCell>Cell 1</TableCell>
<TableCell>Cell 2</TableCell>
<TableCell>Cell 3</TableCell>
</TableRow>
<TableRow>
<TableCell>Cell 4</TableCell>
<TableCell>Cell 5</TableCell>
<TableCell>Cell 6</TableCell>
</TableRow>
<TableRow>
<TableCell>Cell 7</TableCell>
<TableCell>Cell 8</TableCell>
<TableCell>Cell 9</TableCell>
</TableRow>
</tbody>
</Table>
You’ll often want the header to follow as you scroll through the table data so you can remember the context of each column
Name | Status | Role | Location | Time | Start Date | Bio | Actions | |
---|---|---|---|---|---|---|---|---|
![]() Sarah Johnson @sarah | Online | Software Engineer | sarah.johnson@skywardui.com | San Francisco, CA | 21:57GMT-7 | May 10, 2022 | Passionate about coding and building innovative solutions. Enjoys hiking and exploring new technologies. | |
![]() Michael Rodriguez @michael | Offline | Marketing Specialist | michael.rodriguez@skywardui.com | London, UK | 21:57GMT+0 | Sep 22, 2021 | Creative marketer with a knack for social media. Enjoys photography and exploring local coffee shops. | |
![]() Alex Chen @alex | Busy | Data Analyst | alex.chen@skywardui.com | Seattle, WA | 21:57GMT-7 | Jan 8, 2023 | Analytical thinker with a passion for interpreting data. Enjoys playing chess and attending tech meetups. | |
![]() Olivia Thompson @olivia | Online | HR Coordinator | olivia.thompson@skywardui.com | Chicago, IL | 21:57GMT-6 | Nov 14, 2020 | People-oriented professional with a focus on creating a positive workplace. Enjoys reading and yoga. | |
![]() Ryan Patel @ryan | Online | Sales Representative | ryan.patel@skywardui.com | Miami, FL | 21:57GMT-5 | Mar 5, 2022 | Results-driven salesperson with a passion for building client relationships. Enjoys water sports and traveling. | |
![]() Emily Davis @emily | Away | UX/UI Designer | emily.davis@skywardui.com | Los Angeles, CA | 21:57GMT-7 | Jul 18, 2021 | Creative designer with an eye for user experience. Enjoys painting and attending design conferences. | |
![]() Kevin Lewis @kevin | Offline | Financial Analyst | kevin.lewis@skywardui.com | Houston, TX | 21:57GMT-6 | Sep 30, 2022 | Detail-oriented finance professional with a passion for number crunching. Enjoys playing guitar and hiking. | |
![]() Jessica Kim @jessica | Online | Customer Support Specialist | jessica.kim@skywardui.com | Denver, CO | 21:57GMT-6 | Apr 12, 2023 | Customer-focused professional with excellent problem-solving skills. Enjoys gardening and volunteering. | |
![]() Brian Foster @brian | Away | Project Manager | brian.foster@skywardui.com | Atlanta, GA | 21:57GMT-5 | Jan 25, 2021 | Organized project manager with a focus on delivering results. Enjoys hiking and playing golf. | |
![]() Amanda Ramirez @amanda | Online | Content Writer | amanda.ramirez@skywardui.com | Austin, TX | 21:57GMT-6 | Jun 7, 2022 | Creative writer with a passion for storytelling. Enjoys attending book clubs and exploring local breweries. |
Sometimes you’ll want the first column to follow as you scroll through the table data so you can remember the context of each row
Name | Status | Role | Location | Time | Start Date | Bio | Actions | |
---|---|---|---|---|---|---|---|---|
![]() Sarah Johnson @sarah | Online | Software Engineer | sarah.johnson@skywardui.com | San Francisco, CA | 21:57GMT-7 | May 10, 2022 | Passionate about coding and building innovative solutions. Enjoys hiking and exploring new technologies. | |
![]() Michael Rodriguez @michael | Offline | Marketing Specialist | michael.rodriguez@skywardui.com | London, UK | 21:57GMT+0 | Sep 22, 2021 | Creative marketer with a knack for social media. Enjoys photography and exploring local coffee shops. | |
![]() Alex Chen @alex | Busy | Data Analyst | alex.chen@skywardui.com | Seattle, WA | 21:57GMT-7 | Jan 8, 2023 | Analytical thinker with a passion for interpreting data. Enjoys playing chess and attending tech meetups. | |
![]() Olivia Thompson @olivia | Online | HR Coordinator | olivia.thompson@skywardui.com | Chicago, IL | 21:57GMT-6 | Nov 14, 2020 | People-oriented professional with a focus on creating a positive workplace. Enjoys reading and yoga. | |
![]() Ryan Patel @ryan | Online | Sales Representative | ryan.patel@skywardui.com | Miami, FL | 21:57GMT-5 | Mar 5, 2022 | Results-driven salesperson with a passion for building client relationships. Enjoys water sports and traveling. | |
![]() Emily Davis @emily | Away | UX/UI Designer | emily.davis@skywardui.com | Los Angeles, CA | 21:57GMT-7 | Jul 18, 2021 | Creative designer with an eye for user experience. Enjoys painting and attending design conferences. | |
![]() Kevin Lewis @kevin | Offline | Financial Analyst | kevin.lewis@skywardui.com | Houston, TX | 21:57GMT-6 | Sep 30, 2022 | Detail-oriented finance professional with a passion for number crunching. Enjoys playing guitar and hiking. | |
![]() Jessica Kim @jessica | Online | Customer Support Specialist | jessica.kim@skywardui.com | Denver, CO | 21:57GMT-6 | Apr 12, 2023 | Customer-focused professional with excellent problem-solving skills. Enjoys gardening and volunteering. | |
![]() Brian Foster @brian | Away | Project Manager | brian.foster@skywardui.com | Atlanta, GA | 21:57GMT-5 | Jan 25, 2021 | Organized project manager with a focus on delivering results. Enjoys hiking and playing golf. | |
![]() Amanda Ramirez @amanda | Online | Content Writer | amanda.ramirez@skywardui.com | Austin, TX | 21:57GMT-6 | Jun 7, 2022 | Creative writer with a passion for storytelling. Enjoys attending book clubs and exploring local breweries. |
<Table className="relative h-96">
<thead>
<TableRow>
<TableHeaderCell className="sticky left-0 border-r">Name</TableHeaderCell>
<TableHeaderCell>Status</TableHeaderCell>
<TableHeaderCell>Role</TableHeaderCell>
<TableHeaderCell>Email</TableHeaderCell>
<TableHeaderCell>Location</TableHeaderCell>
<TableHeaderCell>Time</TableHeaderCell>
<TableHeaderCell>Start Date</TableHeaderCell>
<TableHeaderCell>Bio</TableHeaderCell>
<TableHeaderCell>Actions</TableHeaderCell>
</TableRow>
</thead>
<tbody>
{employees.map((employee, index) => (
<TableRow>
<TableCell className="sticky left-0 z-10 border-r bg-white dark:bg-gray-950">
<Author
avatar={{
src: employee.avatar,
alt: employee.name,
}}
name={employee.name}
detail={employee.username}
/>
</TableCell>
<TableCell>
<Badge
color={
{
Online: "green",
Offline: "gray",
Busy: "red",
Away: "yellow",
}[employee.status]
}
dot
>
{employee.status}
</Badge>
</TableCell>
<TableCell>{employee.role}</TableCell>
<TableCell>{employee.email}</TableCell>
<TableCell>
<Badge color="gray" transparent>
{employee.location}
</Badge>
</TableCell>
<TableCell className="flex flex-col">
<span>{`${new Date().getHours()}:${new Date().getMinutes()}`}</span>
<span className="text-xs">{employee.timezone}</span>
</TableCell>
<TableCell>
{new Date(employee.startDate).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
})}
</TableCell>
<TableCell className="max-w-xs text-sm">
<span className="line-clamp-2">{employee.bio}</span>
</TableCell>
<TableCell className="space-x-2">
<Button variant="ghost" title="message">
<MessageChatCircleIcon />
<span className="sr-only">Message</span>
</Button>
<Button variant="ghost" title="edit">
<Edit05Icon />
<span className="sr-only">Edit</span>
</Button>
<Button variant="ghost" title="archive">
<ArchiveIcon />
<span className="sr-only">Archive</span>
</Button>
</TableCell>
</TableRow>
))}
</tbody>
</Table>
Requires JS
Sort functionality is not built into the table, but you can add your own by passing a function to the onClick
prop of the TableHeaderCell
component which sorts your data.
Name | Status | Role | Location | Time | Start Date | Bio | Actions | |
---|---|---|---|---|---|---|---|---|
![]() Sarah Johnson @sarah | Online | Software Engineer | sarah.johnson@skywardui.com | San Francisco, CA | 21:57GMT-7 | May 10, 2022 | Passionate about coding and building innovative solutions. Enjoys hiking and exploring new technologies. | |
![]() Michael Rodriguez @michael | Offline | Marketing Specialist | michael.rodriguez@skywardui.com | London, UK | 21:57GMT+0 | Sep 22, 2021 | Creative marketer with a knack for social media. Enjoys photography and exploring local coffee shops. | |
![]() Alex Chen @alex | Busy | Data Analyst | alex.chen@skywardui.com | Seattle, WA | 21:57GMT-7 | Jan 8, 2023 | Analytical thinker with a passion for interpreting data. Enjoys playing chess and attending tech meetups. | |
![]() Olivia Thompson @olivia | Online | HR Coordinator | olivia.thompson@skywardui.com | Chicago, IL | 21:57GMT-6 | Nov 14, 2020 | People-oriented professional with a focus on creating a positive workplace. Enjoys reading and yoga. | |
![]() Ryan Patel @ryan | Online | Sales Representative | ryan.patel@skywardui.com | Miami, FL | 21:57GMT-5 | Mar 5, 2022 | Results-driven salesperson with a passion for building client relationships. Enjoys water sports and traveling. | |
![]() Emily Davis @emily | Away | UX/UI Designer | emily.davis@skywardui.com | Los Angeles, CA | 21:57GMT-7 | Jul 18, 2021 | Creative designer with an eye for user experience. Enjoys painting and attending design conferences. | |
![]() Kevin Lewis @kevin | Offline | Financial Analyst | kevin.lewis@skywardui.com | Houston, TX | 21:57GMT-6 | Sep 30, 2022 | Detail-oriented finance professional with a passion for number crunching. Enjoys playing guitar and hiking. | |
![]() Jessica Kim @jessica | Online | Customer Support Specialist | jessica.kim@skywardui.com | Denver, CO | 21:57GMT-6 | Apr 12, 2023 | Customer-focused professional with excellent problem-solving skills. Enjoys gardening and volunteering. | |
![]() Brian Foster @brian | Away | Project Manager | brian.foster@skywardui.com | Atlanta, GA | 21:57GMT-5 | Jan 25, 2021 | Organized project manager with a focus on delivering results. Enjoys hiking and playing golf. | |
![]() Amanda Ramirez @amanda | Online | Content Writer | amanda.ramirez@skywardui.com | Austin, TX | 21:57GMT-6 | Jun 7, 2022 | Creative writer with a passion for storytelling. Enjoys attending book clubs and exploring local breweries. |
import { useState } from "react"
import {
ArchiveIcon,
ChevronDownIcon,
ChevronUpIcon,
Edit05Icon,
MessageChatCircleIcon,
} from "@untitledui-icons/react/line"
import { Author } from "#/ui/components/Author"
import { Badge } from "#/ui/components/Badge"
import { Button } from "#/ui/components/Button"
import {
Table,
TableCell,
TableHeaderCell,
TableRow,
} from "#/ui/components/Table"
import { cn } from "#/utils"
export type employeeProps = {
name: string
username: string
avatar: string
status: "Online" | "Offline" | "Away" | "Busy"
role: string
email: string
location: string
timezone: string
startDate: string
bio: string
}
export const employees: employeeProps[] = [
{
name: "Sarah Johnson",
username: "@sarah",
avatar: "/avatars/Image-3.jpg",
status: "Online",
role: "Software Engineer",
email: "sarah.johnson@skywardui.com",
location: "San Francisco, CA",
timezone: "GMT-7",
startDate: "2022-05-10",
bio: "Passionate about coding and building innovative solutions. Enjoys hiking and exploring new technologies.",
},
{
name: "Michael Rodriguez",
username: "@michael",
avatar: "/avatars/Image-4.jpg",
status: "Offline",
role: "Marketing Specialist",
email: "michael.rodriguez@skywardui.com",
location: "London, UK",
timezone: "GMT+0",
startDate: "2021-09-22",
bio: "Creative marketer with a knack for social media. Enjoys photography and exploring local coffee shops.",
},
{
name: "Alex Chen",
username: "@alex",
avatar: "/avatars/Image-2.jpg",
status: "Busy",
role: "Data Analyst",
email: "alex.chen@skywardui.com",
location: "Seattle, WA",
timezone: "GMT-7",
startDate: "2023-01-08",
bio: "Analytical thinker with a passion for interpreting data. Enjoys playing chess and attending tech meetups.",
},
{
name: "Olivia Thompson",
username: "@olivia",
avatar: "/avatars/Image-5.jpg",
status: "Online",
role: "HR Coordinator",
email: "olivia.thompson@skywardui.com",
location: "Chicago, IL",
timezone: "GMT-6",
startDate: "2020-11-14",
bio: "People-oriented professional with a focus on creating a positive workplace. Enjoys reading and yoga.",
},
{
name: "Ryan Patel",
username: "@ryan",
avatar: "/avatars/Image-1.jpg",
status: "Online",
role: "Sales Representative",
email: "ryan.patel@skywardui.com",
location: "Miami, FL",
timezone: "GMT-5",
startDate: "2022-03-05",
bio: "Results-driven salesperson with a passion for building client relationships. Enjoys water sports and traveling.",
},
{
name: "Emily Davis",
username: "@emily",
avatar: "/avatars/Image-18.jpg",
status: "Away",
role: "UX/UI Designer",
email: "emily.davis@skywardui.com",
location: "Los Angeles, CA",
timezone: "GMT-7",
startDate: "2021-07-18",
bio: "Creative designer with an eye for user experience. Enjoys painting and attending design conferences.",
},
{
name: "Kevin Lewis",
username: "@kevin",
avatar: "/avatars/Image-7.jpg",
status: "Offline",
role: "Financial Analyst",
email: "kevin.lewis@skywardui.com",
location: "Houston, TX",
timezone: "GMT-6",
startDate: "2022-09-30",
bio: "Detail-oriented finance professional with a passion for number crunching. Enjoys playing guitar and hiking.",
},
{
name: "Jessica Kim",
username: "@jessica",
avatar: "/avatars/Image-1.jpg",
status: "Online",
role: "Customer Support Specialist",
email: "jessica.kim@skywardui.com",
location: "Denver, CO",
timezone: "GMT-6",
startDate: "2023-04-12",
bio: "Customer-focused professional with excellent problem-solving skills. Enjoys gardening and volunteering.",
},
{
name: "Brian Foster",
username: "@brian",
avatar: "/avatars/Image-8.jpg",
status: "Away",
role: "Project Manager",
email: "brian.foster@skywardui.com",
location: "Atlanta, GA",
timezone: "GMT-5",
startDate: "2021-01-25",
bio: "Organized project manager with a focus on delivering results. Enjoys hiking and playing golf.",
},
{
name: "Amanda Ramirez",
username: "@amanda",
avatar: "/avatars/Image-10.jpg",
status: "Online",
role: "Content Writer",
email: "amanda.ramirez@skywardui.com",
location: "Austin, TX",
timezone: "GMT-6",
startDate: "2022-06-07",
bio: "Creative writer with a passion for storytelling. Enjoys attending book clubs and exploring local breweries.",
},
]
export const SortableTable = () => {
const [data, setData] = useState(employees)
const [currentSort, setCurrentSort] = useState<{
key: string
order: "asc" | "desc" | ""
}>()
const sortData = (key: string) => {
const startOrder = currentSort?.key === key ? currentSort?.order : ""
const nextOrder =
startOrder === "" ? "asc" : startOrder === "asc" ? "desc" : ""
setCurrentSort({
key,
order: nextOrder,
})
if (!nextOrder) {
// reset to default order if not asc or desc
setData(employees)
} else {
setData(
// Don't mutate the original array
[...employees].sort((a, b) => {
if (nextOrder === "desc") return a[key] < b[key] ? 1 : -1
return a[key] > b[key] ? 1 : -1
})
)
}
}
return (
<Table>
<thead>
<TableRow>
<TableHeaderCell
onClick={() => sortData("name")}
className="flex justify-between items-center"
>
Name
<SortIndicator
order={currentSort?.key === "name" ? currentSort?.order : ""}
/>
</TableHeaderCell>
<TableHeaderCell>Status</TableHeaderCell>
<TableHeaderCell
onClick={() => sortData("role")}
className="flex justify-between items-center"
>
Role
<SortIndicator
order={currentSort?.key === "role" ? currentSort?.order : ""}
/>
</TableHeaderCell>
<TableHeaderCell>Email</TableHeaderCell>
<TableHeaderCell
onClick={() => sortData("location")}
className="flex justify-between items-center"
>
Location
<SortIndicator
order={currentSort?.key === "location" ? currentSort?.order : ""}
/>
</TableHeaderCell>
<TableHeaderCell>Time</TableHeaderCell>
<TableHeaderCell
onClick={() => sortData("startDate")}
className="flex justify-between items-center"
>
Start Date
<SortIndicator
order={currentSort?.key === "startDate" ? currentSort?.order : ""}
/>
</TableHeaderCell>
<TableHeaderCell>Bio</TableHeaderCell>
<TableHeaderCell>Actions</TableHeaderCell>
</TableRow>
</thead>
<tbody>
{data.map((employee) => (
<TableRow>
<TableCell>
<Author
avatar={{
src: employee.avatar,
alt: employee.name,
}}
name={employee.name}
detail={employee.username}
/>
</TableCell>
<TableCell>
<Badge
color={
{
Online: "green",
Offline: "gray",
Busy: "red",
Away: "yellow",
}[employee.status]
}
dot
>
{employee.status}
</Badge>
</TableCell>
<TableCell>{employee.role}</TableCell>
<TableCell>{employee.email}</TableCell>
<TableCell>
<Badge color="gray" transparent>
{employee.location}
</Badge>
</TableCell>
<TableCell className="flex flex-col">
<span>{`${new Date().getHours()}:${new Date().getMinutes()}`}</span>
<span className="text-xs">{employee.timezone}</span>
</TableCell>
<TableCell>
{new Date(employee.startDate).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
})}
</TableCell>
<TableCell className="text-sm max-w-xs">
<span className="line-clamp-2">{employee.bio}</span>
</TableCell>
<TableCell className="space-x-2">
<Button variant="ghost" title="message">
<MessageChatCircleIcon />
<span className="sr-only">Message</span>
</Button>
<Button variant="ghost" title="edit">
<Edit05Icon />
<span className="sr-only">Edit</span>
</Button>
<Button variant="ghost" title="archive">
<ArchiveIcon />
<span className="sr-only">Archive</span>
</Button>
</TableCell>
</TableRow>
))}
</tbody>
</Table>
)
}
const SortIndicator = ({ order }: { order: "asc" | "desc" | "" }) => (
<Button variant="ghost" size="sm" className="flex flex-col gap-0 -my-2 group">
<ChevronUpIcon
className={cn(
"size-4 -mb-0.5",
order !== "asc" && "opacity-50 group-hover:opacity-100"
)}
/>
<ChevronDownIcon
className={cn(
"size-4 -mt-0.5",
order !== "desc" && "opacity-50 group-hover:opacity-100"
)}
/>
</Button>
)
// just the sorting bits
const [data, setData] = useState(employees)
const [currentSort, setCurrentSort] = useState<{
key: string
order: "asc" | "desc" | ""
}>()
const sortData = (key: string) => {
const startOrder = currentSort?.key === key ? currentSort?.order : ""
const nextOrder =
startOrder === "" ? "asc" : startOrder === "asc" ? "desc" : ""
setCurrentSort({
key,
order: nextOrder,
})
if (!nextOrder) {
// reset to default order if not asc or desc
setData(employees)
} else {
setData(
// Don't mutate the original array
[...employees].sort((a, b) => {
if (nextOrder === "desc") return a[key] < b[key] ? 1 : -1
return a[key] > b[key] ? 1 : -1
})
)
}
}
<TableHeaderCell
onClick={() => sortData("name")}
className="flex justify-between items-center"
>
Name
<SortIcon />
</TableHeaderCell>