Platform Foundationsv0.2.0

Data Table

A slot-based table with sorting, drag-to-group, and column reorder or show/hide.

Stack: .NET(2)
Legacy.NETEnterpriseDeprecated
Hub.NETEnterpriseDeprecated
Stack: Next.js(3)
DocsNext.jsPlatformActive
InsightsNext.jsDataActive
PulseNext.jsDataActive
Stack: React(3)
NumaReactPlatformActive
AtlasReactPlatformActive
FlowReactPlatformActive

When to use

Reach for this when a Platform screen needs a working grid: click-to-sort, drag-to-group with collapsible sections and aggregates, column reorder, show/hide columns, and that arrangement remembered per user. If none of those apply, the plain Table is the right answer. If you need row virtualisation, server-driven pagination or row selection, build on TanStack Table directly instead of stretching this component.

Platform notes

The package carries no shadcn, i18n or icon dependency: you supply slots (Table, Button, DropdownMenu), icons and texts, so the wired wrapper lives in shared/ui and texts is fed from useTranslation, including the function entries (stopGrouping, columnsCount). Sorting and grouping are computed client-side over the rows array you pass, so under server-side paging it only orders the current page: sort on the server and hand it defaultSort to match. Column order, visibility and grouping persist in localStorage under dt:<tableId>:*, which makes tableId a stable contract (reusing one across two grids crosses their state, and renaming a column key orphans the saved entry). On keyboard, header sorting is a real button, but column reorder and adding a group are HTML5 drag only and group rows toggle on row click, so give keyboard users another route to those if the grid is a primary workflow.

Install

pnpm add @plainconceptsplatform/ui-components

Import from the sub-path: @plainconceptsplatform/ui-components/data-table.

Code

"use client";

import { Badge } from "@/components/ui/badge";
import { DataTable } from "@/components/ui/data-table";
import type { DataTableColumn } from "@plainconceptsplatform/ui-components/data-table";

type App = {
  id: string;
  name: string;
  stack: string;
  status: "Active" | "Deprecated";
  team: string;
};

const apps: App[] = [
  { id: "1", name: "Numa", stack: "React", status: "Active", team: "Platform" },
  { id: "2", name: "Docs", stack: "Next.js", status: "Active", team: "Platform" },
  { id: "3", name: "Legacy", stack: ".NET", status: "Deprecated", team: "Enterprise" },
  { id: "4", name: "Insights", stack: "Next.js", status: "Active", team: "Data" },
  { id: "5", name: "Atlas", stack: "React", status: "Active", team: "Platform" },
  { id: "6", name: "Pulse", stack: "Next.js", status: "Active", team: "Data" },
  { id: "7", name: "Hub", stack: ".NET", status: "Deprecated", team: "Enterprise" },
  { id: "8", name: "Flow", stack: "React", status: "Active", team: "Platform" },
];

const columns: DataTableColumn<App>[] = [
  {
    key: "name",
    label: "App",
    render: (r) => <span className="font-medium">{r.name}</span>,
    sortValue: (r) => r.name,
    groupValue: (r) => r.stack,
  },
  {
    key: "stack",
    label: "Stack",
    render: (r) => r.stack,
    sortValue: (r) => r.stack,
  },
  {
    key: "team",
    label: "Team",
    render: (r) => r.team,
    sortValue: (r) => r.team,
  },
  {
    key: "status",
    label: "Status",
    render: (r) => (
      <Badge variant={r.status === "Active" ? "default" : "secondary"}>{r.status}</Badge>
    ),
    sortValue: (r) => r.status,
  },
];

export function DataTableDemo() {
  return (
    <div className="w-full">
      <DataTable
        tableId="docs-data-table-demo"
        rows={apps}
        rowKey={(r) => r.id}
        columns={columns}
        defaultGroupBy={["stack"]}
      />
    </div>
  );
}

Full API

packages/ui-components README