Table
If you can put it in a database, you can put it in a table.
| Name | Access | |
|---|---|---|
| Leslie Alexander | leslie.alexander@example.com | Admin |
| Michael Foster | michael.foster@example.com | Owner |
| Dries Vincent | dries.vincent@example.com | Member |
| Lindsay Walton | lindsay.walton@example.com | Member |
| Courtney Henry | courtney.henry@example.com | Admin |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table>
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table>
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
Component API
| Prop | Default | Description |
|---|---|---|
Table extends the JSX <table> element | ||
bleed | false | Whether the table should bleed into the gutter. |
dense | false | Whether the table should use condensed spacing. |
grid | false | Whether display vertical grid lines. |
striped | false | Whether display striped table rows. |
TableHead extends the JSX <thead> element | ||
| This component does not expose any component-specific props. | ||
TableBody extends the JSX <tbody> element | ||
| This component does not expose any component-specific props. | ||
TableRow extends the JSX <tr> element | ||
href | - | The URL for the row when used as a link. |
target | - | The target for the row when used as a link. |
title | - | The title for the row whe used as a link. |
TableHeader extends the JSX <th> element | ||
| This component does not expose any component-specific props. | ||
TableCell extends the JSX <td> element | ||
| This component does not expose any component-specific props. | ||
Examples
Basic example
Use the Table, TableHead, TableBody, TableRow, TableHeader, and TableCell components to build a table:
| Name | Access | |
|---|---|---|
| Leslie Alexander | leslie.alexander@example.com | Admin |
| Michael Foster | michael.foster@example.com | Owner |
| Dries Vincent | dries.vincent@example.com | Member |
| Lindsay Walton | lindsay.walton@example.com | Member |
| Courtney Henry | courtney.henry@example.com | Admin |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table>
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table>
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
Responsive tables
Tables automatically become scrollable when they are wider than their container:
| Name | Handle | Role | Access | |
|---|---|---|---|---|
| Leslie Alexander | @lesliealexander | Co-Founder / CEO | leslie.alexander@example.com | Admin |
| Michael Foster | @michaelfoster | Co-Founder / CTO | michael.foster@example.com | Owner |
| Dries Vincent | @driesvincent | Business Relations | dries.vincent@example.com | Member |
| Lindsay Walton | @lindsaywalton | Front-end Developer | lindsay.walton@example.com | Member |
| Courtney Henry | @courtneyhenry | Designer | courtney.henry@example.com | Admin |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Handle</TableHeader>
<TableHeader>Role</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Access</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>@{user.handle}</TableCell>
<TableCell>{user.role}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Handle</TableHeader>
<TableHeader>Role</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Access</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>@{user.handle}</TableCell>
<TableCell>{user.role}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
Set the CSS --gutter variable to match the padding of the containing element to make sure the table isn't cropped
unnecessarily when it becomes scrollable. You can change the gutter responsively using media query variants, such as
sm:[--gutter:theme(spacing.4)].
Full-width tables
Use the bleed prop and set the CSS --gutter variable to match the padding of the containing element to make a table
full-width:
| Name | Access | |
|---|---|---|
| Leslie Alexander | leslie.alexander@example.com | Admin |
| Michael Foster | michael.foster@example.com | Owner |
| Dries Vincent | dries.vincent@example.com | Member |
| Lindsay Walton | lindsay.walton@example.com | Member |
| Courtney Henry | courtney.henry@example.com | Admin |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table bleed className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table bleed className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
Full-width tables are still responsive and will become scrollable if they don't fit within the containing element.
Rows as links
Use the href prop on the TableRow component to treat an entire row like a link:
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle} href={user.url}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle} href={user.url}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
When used as a link, TableRow also accepts the target and title props like a regular link.
With condensed spacing
Use the dense prop to render the table with condensed spacing:
| Rank | Player | Pos | GP | G | A | P | +/- |
|---|---|---|---|---|---|---|---|
| 1 | Mitchell Marner | R | 80 | 30 | 69 | 99 | +18 |
| 2 | William Nylander | R | 82 | 40 | 47 | 87 | +10 |
| 3 | Auston Matthews | C | 74 | 40 | 45 | 85 | +31 |
| 4 | John Tavares | C | 80 | 36 | 44 | 80 | -7 |
| 5 | Michael Bunting | L | 82 | 23 | 26 | 49 | +21 |
| 6 | Morgan Rielly | D | 65 | 4 | 37 | 41 | -9 |
| 7 | Calle Jarnkrok | C | 73 | 20 | 19 | 39 | +9 |
| 8 | Alex Kerfoot | C | 82 | 10 | 22 | 32 | +8 |
| 9 | David Kampf | C | 82 | 7 | 20 | 27 | +6 |
| 10 | Mark Giordano | D | 78 | 4 | 20 | 24 | +27 |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ players }) {
return (
<Table dense className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Rank</TableHeader>
<TableHeader>Player</TableHeader>
<TableHeader className="text-right">Pos</TableHeader>
<TableHeader className="text-right">GP</TableHeader>
<TableHeader className="text-right">G</TableHeader>
<TableHeader className="text-right">A</TableHeader>
<TableHeader className="text-right">P</TableHeader>
<TableHeader className="text-right">+/-</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{players.map((player) => (
<TableRow key={player.rank}>
<TableCell className="tabular-nums">{player.rank}</TableCell>
<TableCell className="font-medium">{player.name}</TableCell>
<TableCell className="text-right">{player.position}</TableCell>
<TableCell className="text-right tabular-nums">{player.gamesPlayed}</TableCell>
<TableCell className="text-right tabular-nums">{player.goals}</TableCell>
<TableCell className="text-right tabular-nums">{player.assists}</TableCell>
<TableCell className="text-right tabular-nums">{player.points}</TableCell>
<TableCell className="text-right tabular-nums">{player.plusMinus}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ players }) {
return (
<Table dense className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Rank</TableHeader>
<TableHeader>Player</TableHeader>
<TableHeader className="text-right">Pos</TableHeader>
<TableHeader className="text-right">GP</TableHeader>
<TableHeader className="text-right">G</TableHeader>
<TableHeader className="text-right">A</TableHeader>
<TableHeader className="text-right">P</TableHeader>
<TableHeader className="text-right">+/-</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{players.map((player) => (
<TableRow key={player.rank}>
<TableCell className="tabular-nums">{player.rank}</TableCell>
<TableCell className="font-medium">{player.name}</TableCell>
<TableCell className="text-right">{player.position}</TableCell>
<TableCell className="text-right tabular-nums">{player.gamesPlayed}</TableCell>
<TableCell className="text-right tabular-nums">{player.goals}</TableCell>
<TableCell className="text-right tabular-nums">{player.assists}</TableCell>
<TableCell className="text-right tabular-nums">{player.points}</TableCell>
<TableCell className="text-right tabular-nums">{player.plusMinus}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
With grid lines
Use the grid prop to render the table with vertical grid lines:
| Name | Access | |
|---|---|---|
| Leslie Alexander | leslie.alexander@example.com | Admin |
| Michael Foster | michael.foster@example.com | Owner |
| Dries Vincent | dries.vincent@example.com | Member |
| Lindsay Walton | lindsay.walton@example.com | Member |
| Courtney Henry | courtney.henry@example.com | Admin |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table grid className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table grid className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
With striped rows
Use the striped prop to render the table with striped rows and no horizontal borders:
| Name | Access | |
|---|---|---|
| Leslie Alexander | leslie.alexander@example.com | Admin |
| Michael Foster | michael.foster@example.com | Owner |
| Dries Vincent | dries.vincent@example.com | Member |
| Lindsay Walton | lindsay.walton@example.com | Member |
| Courtney Henry | courtney.henry@example.com | Admin |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table striped className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table striped className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
With different heading color
Use the text-{color} utilities on the TableRow component inside your TableHead to change the color of table
headings:
| Name | Access | |
|---|---|---|
| Leslie Alexander | leslie.alexander@example.com | Admin |
| Michael Foster | michael.foster@example.com | Owner |
| Dries Vincent | dries.vincent@example.com | Member |
| Lindsay Walton | lindsay.walton@example.com | Member |
| Courtney Henry | courtney.henry@example.com | Admin |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow className="text-zinc-950 dark:text-white">
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<Table className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow className="text-zinc-950 dark:text-white">
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
With complex content
Tables are unopinionated about their content and will adapt to just about anything you include:
| Name | Role | Status |
|---|---|---|
Leslie Alexander | Admin | Online |
Michael Foster | Owner | Online |
Dries Vincent | Member | Offline |
Lindsay Walton | Member | Online |
Courtney Henry | Admin | Online |
import { Avatar } from '@/components/avatar'
import { Badge } from '@/components/badge'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
export function ComplexExample({ users }) {
return (
<Table className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Role</TableHeader>
<TableHeader>Status</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell>
<div className="flex items-center gap-4">
<Avatar src={user.avatarUrl} className="size-12" />
<div>
<div className="font-medium">{user.name}</div>
<div className="text-zinc-500">
<a href="#" className="hover:text-zinc-700">
{user.email}
</a>
</div>
</div>
</div>
</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
<TableCell>
{user.online ? <Badge color="lime">Online</Badge> : <Badge color="zinc">Offline</Badge>}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
import { Avatar } from '@/components/avatar'
import { Badge } from '@/components/badge'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
export function ComplexExample({ users }) {
return (
<Table className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Role</TableHeader>
<TableHeader>Status</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell>
<div className="flex items-center gap-4">
<Avatar src={user.avatarUrl} className="size-12" />
<div>
<div className="font-medium">{user.name}</div>
<div className="text-zinc-500">
<a href="#" className="hover:text-zinc-700">
{user.email}
</a>
</div>
</div>
</div>
</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
<TableCell>
{user.online ? <Badge color="lime">Online</Badge> : <Badge color="zinc">Offline</Badge>}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
With pagination
Add a Pagination component below your table to add pagination controls:
| Name | Access | |
|---|---|---|
| Leslie Alexander | leslie.alexander@example.com | Admin |
| Michael Foster | michael.foster@example.com | Owner |
| Dries Vincent | dries.vincent@example.com | Member |
| Lindsay Walton | lindsay.walton@example.com | Member |
| Courtney Henry | courtney.henry@example.com | Admin |
import {
Pagination,
PaginationGap,
PaginationList,
PaginationNext,
PaginationPage,
PaginationPrevious,
} from '@/components/pagination'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<>
<h1 className="mb-6 text-base font-semibold">Users</h1>
<Table>
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Access</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<Pagination className="mt-6">
<PaginationPrevious href="?page=2" />
<PaginationList>
<PaginationPage href="?page=1">1</PaginationPage>
<PaginationPage href="?page=2">2</PaginationPage>
<PaginationPage href="?page=3" current>
3
</PaginationPage>
<PaginationPage href="?page=4">4</PaginationPage>
<PaginationGap />
<PaginationPage href="?page=65">65</PaginationPage>
<PaginationPage href="?page=66">66</PaginationPage>
</PaginationList>
<PaginationNext href="?page=4" />
</Pagination>
</>
)
}
import {
Pagination,
PaginationGap,
PaginationList,
PaginationNext,
PaginationPage,
PaginationPrevious,
} from '@/components/pagination'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
function Example({ users }) {
return (
<>
<h1 className="mb-6 text-base font-semibold">Users</h1>
<Table>
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Access</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<Pagination className="mt-6">
<PaginationPrevious href="?page=2" />
<PaginationList>
<PaginationPage href="?page=1">1</PaginationPage>
<PaginationPage href="?page=2">2</PaginationPage>
<PaginationPage href="?page=3" current>
3
</PaginationPage>
<PaginationPage href="?page=4">4</PaginationPage>
<PaginationGap />
<PaginationPage href="?page=65">65</PaginationPage>
<PaginationPage href="?page=66">66</PaginationPage>
</PaginationList>
<PaginationNext href="?page=4" />
</Pagination>
</>
)
}
Use the mt-* utilities to control the space between the table and the pagination controls.
With dropdowns
Use the Dropdown component within a TableCell to add a dropdown menu:
| Name | Access | Actions | |
|---|---|---|---|
| Leslie Alexander | leslie.alexander@example.com | Admin | |
| Michael Foster | michael.foster@example.com | Owner | |
| Dries Vincent | dries.vincent@example.com | Member | |
| Lindsay Walton | lindsay.walton@example.com | Member | |
| Courtney Henry | courtney.henry@example.com | Admin |
import { Dropdown, DropdownButton, DropdownItem, DropdownMenu } from '@/components/dropdown'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
import { EllipsisHorizontalIcon } from '@heroicons/react/16/solid'
function Example({ users }) {
return (
<Table className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Access</TableHeader>
<TableHeader className="relative w-0">
<span className="sr-only">Actions</span>
</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
<TableCell>
<div className="-mx-3 -my-1.5 sm:-mx-2.5">
<Dropdown>
<DropdownButton plain aria-label="More options">
<EllipsisHorizontalIcon />
</DropdownButton>
<DropdownMenu anchor="bottom end">
<DropdownItem>View</DropdownItem>
<DropdownItem>Edit</DropdownItem>
<DropdownItem>Delete</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
import { Dropdown, DropdownButton, DropdownItem, DropdownMenu } from '@/components/dropdown'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
import { EllipsisHorizontalIcon } from '@heroicons/react/16/solid'
function Example({ users }) {
return (
<Table className="[--gutter:theme(spacing.6)] sm:[--gutter:theme(spacing.8)]">
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Access</TableHeader>
<TableHeader className="relative w-0">
<span className="sr-only">Actions</span>
</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
<TableCell>
<div className="-mx-3 -my-1.5 sm:-mx-2.5">
<Dropdown>
<DropdownButton plain aria-label="More options">
<EllipsisHorizontalIcon />
</DropdownButton>
<DropdownMenu anchor="bottom end">
<DropdownItem>View</DropdownItem>
<DropdownItem>Edit</DropdownItem>
<DropdownItem>Delete</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
When adding elements like dropdowns to a table (especially with the plain style), consider using negative margins to
avoid increasing the size of the table cell. For instance, in the example above we've added -my-1.5 to make sure the
dropdown only takes up 24px of vertical space in the actual layout, which matches the height of the text in the other
cells.
In dialog
Add a Table to your DialogBody component to include a table in a dialog:
import { Button } from '@/components/button'
import { Dialog, DialogActions, DialogBody, DialogDescription, DialogTitle } from '@/components/dialog'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
import { useState } from 'react'
function Example({ users }) {
let [isOpen, setIsOpen] = useState(false)
return (
<>
<Button type="button" onClick={() => setIsOpen(true)}>
Show users
</Button>
<Dialog open={isOpen} onClose={setIsOpen} size="3xl">
<DialogTitle>Users</DialogTitle>
<DialogDescription>The follow users have access to your account.</DialogDescription>
<DialogBody>
<Table bleed compact>
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</DialogBody>
<DialogActions>
<Button onClick={() => setIsOpen(false)}>Close</Button>
</DialogActions>
</Dialog>
</>
)
}
import { Button } from '@/components/button'
import { Dialog, DialogActions, DialogBody, DialogDescription, DialogTitle } from '@/components/dialog'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
import { useState } from 'react'
function Example({ users }) {
let [isOpen, setIsOpen] = useState(false)
return (
<>
<Button type="button" onClick={() => setIsOpen(true)}>
Show users
</Button>
<Dialog open={isOpen} onClose={setIsOpen} size="3xl">
<DialogTitle>Users</DialogTitle>
<DialogDescription>The follow users have access to your account.</DialogDescription>
<DialogBody>
<Table bleed compact>
<TableHead>
<TableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>Role</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.handle}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.access}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</DialogBody>
<DialogActions>
<Button onClick={() => setIsOpen(false)}>Close</Button>
</DialogActions>
</Dialog>
</>
)
}
When using tables within dialogs, the --gutter variable is automatically set to match the dialog's padding.