Quick Start

Minimal Working Example

Getting a fully functional table onto the screen requires four steps: define your data interface, declare your column definitions using ColumnDef, provide a static or fetched data array, and pass both to ClientSideTable. The component handles sorting, pagination, row numbering, and column visibility automatically with no additional configuration.

Minimal Table
"use client"

import { ColumnDef } from "@tanstack/react-table"
import { ClientSideTable } from "@/components/table/client-side-table"
import { DataTableColumnHeader } from "@/components/table/data-table-column-header"

interface User {
  id: number
  name: string
  email: string
  status: "active" | "inactive"
}

const columns: ColumnDef<User>[] = [
  {
    accessorKey: "name",
    header: ({ column }) => <DataTableColumnHeader column={column} title="Name" />,
  },
  {
    accessorKey: "email",
    header: ({ column }) => <DataTableColumnHeader column={column} title="Email" />,
  },
  {
    accessorKey: "status",
    header: ({ column }) => <DataTableColumnHeader column={column} title="Status" />,
  },
]

const data: User[] = [
  { id: 1, name: "Alice", email: "alice@example.com", status: "active" },
  { id: 2, name: "Bob", email: "bob@example.com", status: "inactive" },
]

export default function UsersPage() {
  return (
    <ClientSideTable
      data={data}
      columns={columns}
      pageCount={Math.ceil(data.length / 10)}
    />
  )
}
What you get for free
This minimal setup already includes sortable column headers (click any header to cycle ascending → descending → unsorted), client-side pagination with a configurable page-size selector, automatic row numbering in the first column, a column visibility toggle in the toolbar, and a responsive layout that collapses gracefully on smaller viewports. None of these features require additional props.

Adding Search

Pass a searchableColumns array to enable a global search input in the toolbar. Each entry maps a column id to a human-readable title. The table filters rows client-side as the user types, with no debounce configuration required.

Searchable columns
"use client"

import { ColumnDef } from "@tanstack/react-table"
import { ClientSideTable } from "@/components/table/client-side-table"
import { DataTableColumnHeader } from "@/components/table/data-table-column-header"

interface User {
  id: number
  name: string
  email: string
  status: "active" | "inactive"
}

const columns: ColumnDef<User>[] = [
  {
    accessorKey: "name",
    header: ({ column }) => <DataTableColumnHeader column={column} title="Name" />,
  },
  {
    accessorKey: "email",
    header: ({ column }) => <DataTableColumnHeader column={column} title="Email" />,
  },
  {
    accessorKey: "status",
    header: ({ column }) => <DataTableColumnHeader column={column} title="Status" />,
  },
]

// Pass searchableColumns to enable the global search input.
// Each entry maps a column id to its display label.
export default function UsersPage() {
  return (
    <ClientSideTable
      data={data}
      columns={columns}
      pageCount={Math.ceil(data.length / 10)}
      searchableColumns={[
        { id: "name",  title: "Name"  },
        { id: "email", title: "Email" },
      ]}
    />
  )
}

Adding Filters

Pass a filterableColumns array to render faceted filter dropdowns next to the search input. Each entry declares the target column id, a display title, and an options array of { label, value } pairs. Active filters are shown as dismissible badge chips and can be cleared individually or all at once via the reset button.

Filterable columns
"use client"

import { ColumnDef } from "@tanstack/react-table"
import { ClientSideTable } from "@/components/table/client-side-table"
import { DataTableColumnHeader } from "@/components/table/data-table-column-header"

interface User {
  id: number
  name: string
  email: string
  status: "active" | "inactive"
}

const columns: ColumnDef<User>[] = [
  {
    accessorKey: "name",
    header: ({ column }) => <DataTableColumnHeader column={column} title="Name" />,
  },
  {
    accessorKey: "email",
    header: ({ column }) => <DataTableColumnHeader column={column} title="Email" />,
  },
  {
    accessorKey: "status",
    header: ({ column }) => <DataTableColumnHeader column={column} title="Status" />,
  },
]

// filterableColumns enables faceted filter dropdowns in the toolbar.
// Each entry needs an id, a label, and the list of selectable options.
export default function UsersPage() {
  return (
    <ClientSideTable
      data={data}
      columns={columns}
      pageCount={Math.ceil(data.length / 10)}
      filterableColumns={[
        {
          id: "status",
          title: "Status",
          options: [
            { label: "Active",   value: "active"   },
            { label: "Inactive", value: "inactive" },
          ],
        },
      ]}
    />
  )
}

Adding Row Actions

Use the reserved "actions" column id together with the TableActionsRow component to render a per-row dropdown menu. Each action entry accepts a label, an onClick handler, and an optional variant ("default" or "destructive") that controls the text color of the menu item.

Row actions
"use client"

import { ColumnDef } from "@tanstack/react-table"
import { ClientSideTable } from "@/components/table/client-side-table"
import { DataTableColumnHeader } from "@/components/table/data-table-column-header"
import { TableActionsRow } from "@/components/table/table-actions-row"

interface User {
  id: number
  name: string
  email: string
  status: "active" | "inactive"
}

const columns: ColumnDef<User>[] = [
  {
    accessorKey: "name",
    header: ({ column }) => <DataTableColumnHeader column={column} title="Name" />,
  },
  {
    accessorKey: "email",
    header: ({ column }) => <DataTableColumnHeader column={column} title="Email" />,
  },
  {
    accessorKey: "status",
    header: ({ column }) => <DataTableColumnHeader column={column} title="Status" />,
  },
  // The "actions" column id is reserved and renders a dropdown menu per row.
  {
    id: "actions",
    cell: ({ row }) => (
      <TableActionsRow
        actions={[
          {
            label: "Edit",
            onClick: () => console.log("edit", row.original.id),
          },
          {
            label: "Delete",
            onClick: () => console.log("delete", row.original.id),
            variant: "destructive",
          },
        ]}
      />
    ),
  },
]