HextaUI

Data table: Pinned columns

The selection column and the first data column are pinned by default. Choose your own with pinStart. A soft shadow marks the edge once the table scrolls sideways.

MethodCountry
[email protected]paidCardUnited States2026-01-01$5.00
[email protected]pendingPayPalJapan2026-02-02$84.20
[email protected]failedBankGermany2026-03-03$163.40
[email protected]paidApple PayBrazil2026-04-04$242.60
[email protected]pendingCardIndia2026-05-05$321.80
[email protected]failedPayPalUnited States2026-06-06$401.00
[email protected]paidBankJapan2026-07-07$480.10
[email protected]pendingApple PayGermany2026-08-08$559.30
[email protected]failedCardBrazil2026-09-09$638.50
[email protected]paidPayPalIndia2026-01-10$717.70
0 of 12 rows selected
Page 1 of 2
"use client"

import { Badge, BadgeDot } from "@/components/ui/badge"
import {
  createDataTableColumns,
  DataTable,
  DataTableColumnHeader,
  DataTableContent,
  DataTablePagination,
  DataTableSearch,
  DataTableSelectAll,
  DataTableSelectRow,
  DataTableToolbar,
  DataTableViewOptions,
  useDataTable,
} from "@/components/ui/data-table"

type Payment = {
  id: string
  email: string
  status: "paid" | "pending" | "failed"
  amount: number
  method: string
  country: string
  date: string
}

const statuses = ["paid", "pending", "failed"] as const
const methods = ["Card", "PayPal", "Bank", "Apple Pay"]
const countries = ["United States", "Germany", "India", "Japan", "Brazil"]
const names = ["olivia", "jackson", "isabella", "william", "sofia", "liam"]

function makePayments(count: number): Payment[] {
  return Array.from({ length: count }, (_, index) => ({
    id: `pay_${String(index + 1).padStart(4, "0")}`,
    email: `${names[index % names.length]}.${Math.floor(index / names.length) + 1}@example.com`,
    status: statuses[(index * 7) % 3],
    amount: Math.round(((index * 7919) % 100000) / 10) / 10 + 5,
    method: methods[index % methods.length],
    country: countries[(index * 3) % countries.length],
    date: `2026-${String((index % 9) + 1).padStart(2, "0")}-${String((index % 27) + 1).padStart(2, "0")}`,
  }))
}

const payments = makePayments(12)

const currency = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
})

const statusVariant = {
  paid: "success",
  pending: "warning",
  failed: "destructive",
} as const

const helper = createDataTableColumns<Payment>()

const columns = helper.columns([
  helper.display({
    id: "select",
    header: ({ table }) => <DataTableSelectAll table={table} />,
    cell: ({ row }) => <DataTableSelectRow row={row} />,
    enableSorting: false,
    enableHiding: false,
  }),
  helper.accessor("email", {
    header: ({ column }) => (
      <DataTableColumnHeader column={column} title="Email" />
    ),
  }),
  helper.accessor("status", {
    header: ({ column }) => (
      <DataTableColumnHeader column={column} title="Status" />
    ),
    cell: ({ getValue }) => {
      const status = getValue()
      return (
        <Badge variant={statusVariant[status]}>
          <BadgeDot />
          <span className="capitalize">{status}</span>
        </Badge>
      )
    },
  }),
  helper.accessor("method", { header: "Method" }),
  helper.accessor("country", { header: "Country" }),
  helper.accessor("date", {
    header: ({ column }) => (
      <DataTableColumnHeader column={column} title="Date" />
    ),
  }),
  helper.accessor("amount", {
    header: ({ column }) => (
      <DataTableColumnHeader column={column} title="Amount" />
    ),
    cell: ({ getValue }) => currency.format(getValue()),
    meta: { align: "end" },
  }),
])

export function DataTablePinnedColumns() {
  const table = useDataTable({
    data: payments,
    columns,
    getRowId: (row) => row.id,
  })

  return (
    <div className="w-full max-w-md">
      <DataTable table={table}>
        <DataTableToolbar>
          <DataTableSearch placeholder="Search payments…" />
          <DataTableViewOptions />
        </DataTableToolbar>
        <DataTableContent pinStart={["select", "email", "status"]} />
        <DataTablePagination />
      </DataTable>
    </div>
  )
}

This adds the example as components/examples/data-table/pinned-columns.tsx, together with the Data table component and the HextaUI theme tokens it needs.

pnpm dlx shadcn@latest add https://hextaui.com/r/data-table-pinned-columns.json

To add only the component, follow the Data table installation.

Props in this example

The Data table props this example sets, from the API reference.

PropTypeDefault
tableThe table returned by useDataTable.
DataTableInstance<TData>–
PropTypeDefault
pinStartColumn ids to pin to the start edge.
string[]["select", firstColumnId]
PropTypeDefault
placeholder
string"Search…"
PropTypeDefault
column
Column–
title
string–
PropTypeDefault
table
Table–
PropTypeDefault
row
Row–

Every prop is in the Data table API reference.

See them all, with keyboard support and accessibility notes, on the Data table page.