GitHub

Table

Headless, sortable, filterable, and paginated data tables powered by TanStack Table v8. Full control over styling with zero opinion on markup.

TanStack Table v8TypeScriptSortableFilterablePaginated
01

Theme Preview

The table adapts seamlessly to both themes via Tailwind's class-based dark mode. Each variant below is rendered with forced styling — independent of the current app theme — so you can compare them side by side.

Lightbg-white · border-slate-200
NameEmailRoleStatus
Alice Johnsonalice@example.comadminactive
Bob Smithbob@example.comeditoractive
Carol Williamscarol@example.comvieweractive
David Browndavid@example.comeditorinactive
Eva Martinezeva@example.comadminactive
Darkbg-[#0d1117] · border-[#1f2937]
NameEmailRoleStatus
Alice Johnsonalice@example.comadminactive
Bob Smithbob@example.comeditoractive
Carol Williamscarol@example.comvieweractive
David Browndavid@example.comeditorinactive
Eva Martinezeva@example.comadminactive
02

Live Demo

Fully interactive — try sorting columns, filtering rows, and navigating pages. The table responds to the current app theme automatically.

Name
Email
Role
Status
Created
Alice Johnsonalice@example.comadminactiveJan 15, 2024
Bob Smithbob@example.comeditoractiveFeb 10, 2024
Carol Williamscarol@example.comvieweractiveFeb 20, 2024
David Browndavid@example.comeditorinactiveMar 5, 2024
Eva Martinezeva@example.comadminactiveMar 12, 2024
Frank Garciafrank@example.comvieweractiveMar 18, 2024
Grace Leegrace@example.comeditoractiveApr 1, 2024
Henry Wilsonhenry@example.comviewerinactiveApr 10, 2024

Page 1 of 3 (20 rows)

03

Column Config

Define columns with ColumnDef<T>. Wrap in useMemo to prevent re-instantiation on every render.

components/UserTable.tsx
const columns = useMemo<ColumnDef<User>[]>(() => [
  {
    accessorKey: "name",
    header: "Name",
    cell: (info) => (
      <span className="font-medium">{info.getValue<string>()}</span>
    ),
  },
  {
    accessorKey: "role",
    header: "Role",
    cell: (info) => (
      <span className="rounded-full bg-slate-100 px-2.5 py-0.5 text-xs
        font-medium capitalize dark:bg-slate-800">
        {info.getValue<string>()}
      </span>
    ),
  },
  {
    accessorKey: "status",
    header: "Status",
    cell: (info) => {
      const status = info.getValue<string>();
      return (
        <span className={`rounded-full px-2.5 py-0.5 text-xs font-medium ${
          status === "active"
            ? "bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-400"
            : "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-400"
        }`}>
          {status}
        </span>
      );
    },
  },
], []);
04

Props

PropTypeRequiredDescription
dataT[]requiredArray of row data to display in the table.
columnsColumnDef<T>[]requiredColumn definitions including header, accessor, and optional cell renderer.
pageSizenumberoptionalNumber of rows shown per page. Defaults to 8.
globalFilterstringoptionalText filter applied across all column values simultaneously.
sortingSortingStateoptionalControlled sorting state. Pair with onSortingChange for external control.
react-principles