Volt UI
Components

Table

A semantic table component for displaying tabular data with support for headers, footers, and aligned content.

FeatureStatus
Static data display
Pagination
Row actions
Sorting
Filtering
Virtualization

For sorting, filtering, row selection, and virtualization, use the DataTable component which is built on Tanstack Table.

Basic Usage

StatusTypeInvoice NumberDateDue DateAmount
DelayedInstallment rate2024-A-1008.10.202522.10.202585,00 €
DelayedInstallment rate2024-A-0908.09.202522.09.202585,00 €
PaidPayment-10.07.2025-45,00 €
PaidYearly Invoice2024-J-00115.06.202529.06.2025150,00 €
RefundCashback2024-G-00305.05.2025--50,00 €
import {
  Table,
  TableHeader,
  TableBody,
  TableRow,
  TableHead,
  TableCell,
  Badge,
} from "@epilot/volt-ui"
import { IconAlertTriangle, IconCircleCheck } from "@tabler/icons-react"

<Table>
  <TableHeader>
    <TableRow>
      <TableHead>Status</TableHead>
      <TableHead>Type</TableHead>
      <TableHead>Invoice Number</TableHead>
      <TableHead>Date</TableHead>
      <TableHead>Due Date</TableHead>
      <TableHead align="right">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>
        <Badge color="red" style="soft" className="!bg-red-100 !text-red-600 !font-semibold">
          <IconAlertTriangle size={14} />
          Delayed
        </Badge>
      </TableCell>
      <TableCell>Installment rate</TableCell>
      <TableCell>2024-A-10</TableCell>
      <TableCell>08.10.2025</TableCell>
      <TableCell>22.10.2025</TableCell>
      <TableCell align="right" numeric>85,00 €</TableCell>
    </TableRow>
    <TableRow>
      <TableCell>
        <Badge color="red" style="soft" className="!bg-red-100 !text-red-600 !font-semibold">
          <IconAlertTriangle size={14} />
          Delayed
        </Badge>
      </TableCell>
      <TableCell>Installment rate</TableCell>
      <TableCell>2024-A-09</TableCell>
      <TableCell>08.09.2025</TableCell>
      <TableCell>22.09.2025</TableCell>
      <TableCell align="right" numeric>85,00 €</TableCell>
    </TableRow>
    <TableRow>
      <TableCell>
        <Badge color="green" style="soft" className="!font-semibold">
          <IconCircleCheck size={14} />
          Paid
        </Badge>
      </TableCell>
      <TableCell>Payment</TableCell>
      <TableCell>-</TableCell>
      <TableCell>10.07.2025</TableCell>
      <TableCell>-</TableCell>
      <TableCell align="right" numeric>45,00 €</TableCell>
    </TableRow>
    <TableRow>
      <TableCell>
        <Badge color="green" style="soft" className="!font-semibold">
          <IconCircleCheck size={14} />
          Paid
        </Badge>
      </TableCell>
      <TableCell>Yearly Invoice</TableCell>
      <TableCell>2024-J-001</TableCell>
      <TableCell>15.06.2025</TableCell>
      <TableCell>29.06.2025</TableCell>
      <TableCell align="right" numeric>150,00 €</TableCell>
    </TableRow>
    <TableRow>
      <TableCell>
        <Badge color="teal" style="soft" className="!font-semibold">
          <IconCircleCheck size={14} />
          Refund
        </Badge>
      </TableCell>
      <TableCell>Cashback</TableCell>
      <TableCell>2024-G-003</TableCell>
      <TableCell>05.05.2025</TableCell>
      <TableCell>-</TableCell>
      <TableCell align="right" numeric>-50,00 €</TableCell>
    </TableRow>
  </TableBody>
</Table>

Simple Table

NameEmailAmount
John Doejohn@example.com$250.00
Jane Smithjane@example.com$150.00
Bob Wilsonbob@example.com$350.00
Total$750.00
<Table>
  <TableHeader>
    <TableRow>
      <TableHead>Name</TableHead>
      <TableHead>Email</TableHead>
      <TableHead align="right">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>John Doe</TableCell>
      <TableCell>john@example.com</TableCell>
      <TableCell align="right" numeric>$250.00</TableCell>
    </TableRow>
    <TableRow>
      <TableCell>Jane Smith</TableCell>
      <TableCell>jane@example.com</TableCell>
      <TableCell align="right" numeric>$150.00</TableCell>
    </TableRow>
    <TableRow>
      <TableCell>Bob Wilson</TableCell>
      <TableCell>bob@example.com</TableCell>
      <TableCell align="right" numeric>$350.00</TableCell>
    </TableRow>
  </TableBody>
  <TableFooter>
    <TableRow>
      <TableCell colSpan={2}>Total</TableCell>
      <TableCell align="right" numeric>$750.00</TableCell>
    </TableRow>
  </TableFooter>
</Table>

With Row Actions

Use DropdownMenu to add action menus to table rows. The empty TableHead with a fixed width creates space for the actions column.

StatusInvoiceCustomerAmount
Delayed2024-A-10John Doe85.00 €
Paid2024-A-09Jane Smith45.00 €
Paid2024-J-001Bob Wilson150.00 €
import {
  Table,
  TableHeader,
  TableBody,
  TableRow,
  TableHead,
  TableCell,
  Badge,
  Button,
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
} from "@epilot/volt-ui"
import {
  IconDotsVertical,
  IconPencil,
  IconCopy,
  IconTrash,
  IconEye,
  IconAlertTriangle,
  IconCircleCheck,
} from "@tabler/icons-react"

<Table>
  <TableHeader>
    <TableRow>
      <TableHead>Status</TableHead>
      <TableHead>Invoice</TableHead>
      <TableHead>Customer</TableHead>
      <TableHead align="right">Amount</TableHead>
      <TableHead className="w-12"></TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>
        <Badge color="red" style="soft" className="!bg-red-100 !text-red-600 !font-semibold">
          <IconAlertTriangle size={14} />
          Delayed
        </Badge>
      </TableCell>
      <TableCell>2024-A-10</TableCell>
      <TableCell>John Doe</TableCell>
      <TableCell align="right" numeric>85.00 €</TableCell>
      <TableCell>
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button variant="tertiary" size="icon-sm">
              <IconDotsVertical />
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end">
            <DropdownMenuItem>
              <IconEye />
              View
            </DropdownMenuItem>
            <DropdownMenuItem>
              <IconPencil />
              Edit
              <DropdownMenuShortcut>Ctrl+E</DropdownMenuShortcut>
            </DropdownMenuItem>
            <DropdownMenuItem>
              <IconCopy />
              Duplicate
            </DropdownMenuItem>
            <DropdownMenuSeparator />
            <DropdownMenuItem destructive>
              <IconTrash />
              Delete
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      </TableCell>
    </TableRow>
    <TableRow>
      <TableCell>
        <Badge color="green" style="soft" className="!font-semibold">
          <IconCircleCheck size={14} />
          Paid
        </Badge>
      </TableCell>
      <TableCell>2024-A-09</TableCell>
      <TableCell>Jane Smith</TableCell>
      <TableCell align="right" numeric>45.00 €</TableCell>
      <TableCell>
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button variant="tertiary" size="icon-sm">
              <IconDotsVertical />
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end">
            <DropdownMenuItem>
              <IconEye />
              View
            </DropdownMenuItem>
            <DropdownMenuItem>
              <IconPencil />
              Edit
              <DropdownMenuShortcut>Ctrl+E</DropdownMenuShortcut>
            </DropdownMenuItem>
            <DropdownMenuItem>
              <IconCopy />
              Duplicate
            </DropdownMenuItem>
            <DropdownMenuSeparator />
            <DropdownMenuItem destructive>
              <IconTrash />
              Delete
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      </TableCell>
    </TableRow>
    <TableRow>
      <TableCell>
        <Badge color="green" style="soft" className="!font-semibold">
          <IconCircleCheck size={14} />
          Paid
        </Badge>
      </TableCell>
      <TableCell>2024-J-001</TableCell>
      <TableCell>Bob Wilson</TableCell>
      <TableCell align="right" numeric>150.00 €</TableCell>
      <TableCell>
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button variant="tertiary" size="icon-sm">
              <IconDotsVertical />
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end">
            <DropdownMenuItem>
              <IconEye />
              View
            </DropdownMenuItem>
            <DropdownMenuItem>
              <IconPencil />
              Edit
              <DropdownMenuShortcut>Ctrl+E</DropdownMenuShortcut>
            </DropdownMenuItem>
            <DropdownMenuItem>
              <IconCopy />
              Duplicate
            </DropdownMenuItem>
            <DropdownMenuSeparator />
            <DropdownMenuItem destructive>
              <IconTrash />
              Delete
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      </TableCell>
    </TableRow>
  </TableBody>
</Table>

With Pagination

TablePagination is a controlled component - the parent manages all state and data fetching. This design works seamlessly with both server-side pagination (API returns pages) and client-side pagination (local data array).

StatusInvoiceCustomerAmount
Paid2024-A-001John Doe202.00 €
Paid2024-A-002Jane Smith142.00 €
Paid2024-A-003Bob Wilson193.00 €
Paid2024-A-004Alice Brown82.00 €
Paid2024-A-005Charlie Davis86.00 €
Paid2024-A-006Eva Martinez54.00 €
Paid2024-A-007Frank Miller232.00 €
Delayed2024-A-008Grace Lee130.00 €
Paid2024-A-009Henry Chen221.00 €
Delayed2024-A-010Isabel Garcia144.00 €

Client-Side Pagination

For local data arrays, use the useClientPagination hook to handle state and slicing automatically:

import { Table, TablePagination, useClientPagination } from "@epilot/volt-ui"

function MyTable() {
  const { pageData, paginationProps } = useClientPagination({
    data: allItems,        // Your full data array
    initialPageSize: 10,   // Optional, default 10
  })

  return (
    <div>
      <Table>
        {pageData.map(item => (
          <TableRow key={item.id}>...</TableRow>
        ))}
      </Table>
      <TablePagination {...paginationProps} />
    </div>
  )
}

Server-Side Pagination

For paginated APIs (e.g., with TanStack Query), manage state manually and convert page numbers to API offsets:

import { useState } from "react"
import { Table, TablePagination } from "@epilot/volt-ui"
import { useQuery } from "@tanstack/react-query"

function MyTable() {
  const [page, setPage] = useState(1)
  const [pageSize, setPageSize] = useState(10)

  // Convert page to offset for API
  const from = (page - 1) * pageSize

  const { data } = useQuery({
    queryKey: ["items", from, pageSize],
    queryFn: () => fetchItems({ from, size: pageSize }),
  })

  return (
    <div>
      <Table>
        {data?.results.map(item => (
          <TableRow key={item.id}>...</TableRow>
        ))}
      </Table>
      <TablePagination
        page={page}
        totalItems={data?.hits ?? 0}
        pageSize={pageSize}
        onPageChange={setPage}
        onPageSizeChange={(size) => {
          setPageSize(size)
          setPage(1)
        }}
      />
    </div>
  )
}

API Reference

Table

Root container that wraps the native <table> element with responsive overflow handling.

PropTypeDefaultDescription
density"compact" | "normal" | "comfortable""normal"Default cell density for all TableCells.
classNamestring-Additional classes for the container.

TableHeader

Wrapper for the table header section (<thead>).

PropTypeDefaultDescription
classNamestring-Additional classes for the header.

TableBody

Wrapper for the table body section (<tbody>).

PropTypeDefaultDescription
classNamestring-Additional classes for the body.

TableFooter

Wrapper for the table footer section (<tfoot>).

PropTypeDefaultDescription
classNamestring-Additional classes for the footer.

TableRow

A table row (<tr>).

PropTypeDefaultDescription
classNamestring-Additional classes for the row.

TableHead

A header cell (<th>).

PropTypeDefaultDescription
align"left" | "center" | "right""left"Horizontal text alignment.
density"compact" | "normal" | "comfortable"-Override table density. compact (h-8), normal (h-9), comfortable (h-10).
scope"col" | "row" | "colgroup" | "rowgroup""col"Scope of the header for screen readers.
classNamestring-Additional classes for the header.

TableCell

A data cell (<td>).

PropTypeDefaultDescription
align"left" | "center" | "right""left"Horizontal text alignment.
numericbooleanfalseEnables tabular-nums for consistent number widths.
density"compact" | "normal" | "comfortable"-Override table density. compact (py-1.5), normal (py-2), comfortable (py-3).
classNamestring-Additional classes for the cell.

TableCaption

An accessible caption for the table (<caption>).

PropTypeDefaultDescription
position"top" | "bottom""bottom"Position of the caption.
classNamestring-Additional classes for the caption.

TablePagination

Controlled pagination component for tables with page number buttons, navigation controls, and page size selector.

PropTypeDefaultDescription
pagenumber-Current page number (1-indexed).
totalItemsnumber-Total number of items across pages.
pageSizenumber-Number of items per page.
onPageChange(page: number) => void-Callback when page changes.
onPageSizeChange(pageSize: number) => void-Callback when page size changes.
pageSizeOptionsnumber[][10, 25, 50, 100]Available page size options.
siblingCountnumber2Number of sibling pages to show on each side of current page.
disabledbooleanfalseDisable all pagination controls.
classNamestring-Additional classes for the container.

useClientPagination

Hook for client-side pagination of local data arrays. Handles state management and data slicing.

Options:

OptionTypeDefaultDescription
dataT[]-The full data array to paginate.
initialPagenumber1Initial page number.
initialPageSizenumber10Initial page size.
pageSizeOptionsnumber[][10, 25, 50, 100]Available page size options.

Returns:

PropertyTypeDescription
pageDataT[]Sliced data for the current page.
pagenumberCurrent page number.
pageSizenumberCurrent page size.
setPage(page: number) => voidSet the current page.
setPageSize(size: number) => voidSet page size (resets to page 1).
metaPaginationMeta{ totalPages, hasNextPage, ... }
paginationPropsobjectProps to spread on <TablePagination />.