Skip to content
ilinxa/pro-ui

Data Table

alphav0.1.2

A typed, composable table primitive with column accessors and per-cell rendering.

Category: Data DisplayUpdated: 2026-08-11Created: 2026-04-26Author: ilinxa

Context

DataTable is the foundational data display component — every more advanced table (sortable, paginated, virtualized) in this registry is expected to compose on top of it. It is deliberately small: one render, no client state, no DOM-level magic.

Installation

Initialize shadcn (once per project)Seeds lib/utils.ts and components.json. Skip if you've already used any shadcn component.
pnpm dlx shadcn@latest init
Install the component
pnpm dlx shadcn@latest add @ilinxa/data-table

Add -fixtures for dummy data:

pnpm dlx shadcn@latest add @ilinxa/data-table-fixtures

CLI can't resolve @ilinxa? The namespace is listed in the official shadcn registry directory, so current CLIs need no configuration. If yours can't resolve it (older or pinned versions, self-hosted mirrors), register it manually in components.json:

"registries": {
  "@ilinxa": "https://ui.ilinxa.com/r/{name}.json"
}

Preview

A starter table rendered from dummy data.
NameRoleStatusLast Seen
Aria Montgomeryaria@ilinxa.dev
OwnerActive2 minutes ago
Bilal Hashemibilal@ilinxa.dev
AdminActive1 hour ago
Camille Okaforcamille@ilinxa.dev
MemberInvited—
Dimitri Volkovdimitri@ilinxa.dev
MemberActiveYesterday
Esme Tanakaesme@ilinxa.dev
ViewerSuspended3 weeks ago

Demo source

demo.tsxtsx
import { Badge } from "@/components/ui/badge";import { DataTable } from "./data-table";import { DEMO_USERS, type DemoUser } from "./dummy-data";import type { DataTableColumn } from "./types"; const columns: DataTableColumn<DemoUser>[] = [  {    id: "name",    header: "Name",    accessor: (row) => (      <div className="flex flex-col">        <span className="font-medium text-foreground">{row.name}</span>        <span className="text-xs text-muted-foreground">{row.email}</span>      </div>    ),  },  {    id: "role",    header: "Role",    accessor: (row) => <span className="text-foreground">{row.role}</span>,  },  {    id: "status",    header: "Status",    accessor: (row) => {      const variant =        row.status === "Active"          ? "default"          : row.status === "Invited"            ? "secondary"            : "destructive";      return <Badge variant={variant}>{row.status}</Badge>;    },  },  {    id: "lastSeen",    header: "Last Seen",    align: "right",    accessor: (row) => (      <span className="text-sm text-muted-foreground">{row.lastSeen}</span>    ),  },]; export default function DataTableDemo() {  return (    <DataTable      columns={columns}      rows={DEMO_USERS}      rowKey={(row) => row.id}      caption="A starter table rendered from dummy data."    />  );} 

Usage

When to use

Reach for DataTable when you need to render a list of records with consistent column structure. It is intentionally unopinionated about sorting, filtering, and pagination — those concerns compose on top via the column accessor and parent state.

Basic example

import { DataTable, type DataTableColumn } from "@/components/data-table"

type User = { id: string; name: string; email: string }

const columns: DataTableColumn<User>[] = [
  { id: "name",  header: "Name",  accessor: (r) => r.name },
  { id: "email", header: "Email", accessor: (r) => r.email },
]

export function Example({ users }: { users: User[] }) {
  return (
    <DataTable
      columns={columns}
      rows={users}
      rowKey={(r) => r.id}
    />
  )
}

Notes

  • accessor can return any ReactNode, so composing badges, avatars, or actions per cell is the intended path.
  • Column align applies to both header and cells; use width for fixed-width columns (e.g. action menus).
  • Pass an emptyState node to override the default empty message.

Features

  • Generic over row type — fully type-safe column accessors
  • Per-column alignment and fixed widths
  • Custom empty state slot
  • Composes any ReactNode in cells (badges, avatars, actions)

Tags

tabledatalisttyped

Dependencies

shadcn primitives: table