Modern SaaS data table with TanStack Table, inset-card layout, selection, sorting, search, pagination, and expandable rows.
| Status | Confidence | Cost | Tasks | Assignee | |
|---|---|---|---|---|---|
| Researcher | Running | 94% | $0.034 | ||
| Proposal Drafter | Idle | 87% | $0.018 | ||
| Data Extractor | Error | 61 | $0.052 | AG | |
| UX Reviewer | Running | 91% | $0.027 | ||
| Composer | Running | 88% | $0.011 | ||
| Code Reviewer | Scheduled | 96% | $0.041 |
SHOWING: 6 of 35 items
"use client";
import type { ColumnDef } from "@tanstack/react-table";
import {
DataTable,
type DataTableAvatarItem,
DataTableAvatarStack,
DataTableColumnHeader,
DataTableConfidence,
DataTableStatusBadge,
DataTableTaskCount,
} from "@/components/matos-ui/data-table";
type AgentStatus = "running" | "idle" | "error" | "scheduled";
type Agent = {
agent: string;
status: AgentStatus;
confidence: number;
cost: string;
tasks: number;
assignee: DataTableAvatarItem[];
};
const agents: Agent[] = [
{
agent: "Researcher",
status: "running",
confidence: 94,
cost: "$0.034",
tasks: 12,
assignee: [
{
name: "Ana Silva",
image:
"https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=80&h=80&fit=crop&crop=face",
},
],
},
{
agent: "Proposal Drafter",
status: "idle",
confidence: 87,
cost: "$0.018",
tasks: 3,
assignee: [
{
name: "Marina Costa",
image:
"https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=80&h=80&fit=crop&crop=face",
},
{
name: "Lucas Rocha",
image:
"https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=80&h=80&fit=crop&crop=face",
},
{ name: "John Lima", initials: "JL" },
{ name: "Bia Torres", initials: "BT" },
{ name: "Rafa Alves", initials: "RA" },
],
},
{
agent: "Data Extractor",
status: "error",
confidence: 61,
cost: "$0.052",
tasks: 2,
assignee: [{ name: "Agent Group", initials: "AG" }],
},
{
agent: "UX Reviewer",
status: "running",
confidence: 91,
cost: "$0.027",
tasks: 7,
assignee: [
{
name: "Pedro Martins",
image:
"https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?w=80&h=80&fit=crop&crop=face",
},
{
name: "Mateus Dias",
image:
"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=80&h=80&fit=crop&crop=face",
},
{
name: "Carlos Freitas",
image:
"https://images.unsplash.com/photo-1508214751196-bcfd4ca60f91?w=80&h=80&fit=crop&crop=face",
},
],
},
{
agent: "Composer",
status: "running",
confidence: 88,
cost: "$0.011",
tasks: 5,
assignee: [
{
name: "Laura Mendes",
image:
"https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=80&h=80&fit=crop&crop=face",
},
{
name: "Felipe Souza",
image:
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=80&h=80&fit=crop&crop=face",
},
{ name: "Nina Reis", initials: "NR" },
{ name: "Theo Ramos", initials: "TR" },
],
},
{
agent: "Code Reviewer",
status: "scheduled",
confidence: 96,
cost: "$0.041",
tasks: 21,
assignee: [
{
name: "Bruno Lopes",
image:
"https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?w=80&h=80&fit=crop&crop=face",
},
{
name: "Gustavo Lima",
image:
"https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=80&h=80&fit=crop&crop=face",
},
{
name: "Diego Nunes",
image:
"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=80&h=80&fit=crop&crop=face",
},
{ name: "Sofia Dias", initials: "SD" },
{ name: "Luan Castro", initials: "LC" },
{ name: "Eva Moraes", initials: "EM" },
],
},
];
const statusLabel: Record<AgentStatus, string> = {
running: "Running",
idle: "Idle",
error: "Error",
scheduled: "Scheduled",
};
const columns: ColumnDef<Agent>[] = [
{
accessorKey: "agent",
size: 190,
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Agent" />
),
cell: ({ row }) => (
<span className="font-medium text-foreground">{row.original.agent}</span>
),
enableSorting: true,
},
{
accessorKey: "status",
size: 140,
header: "Status",
cell: ({ row }) => (
<DataTableStatusBadge status={row.original.status}>
{statusLabel[row.original.status]}
</DataTableStatusBadge>
),
},
{
accessorKey: "confidence",
size: 170,
header: "Confidence",
cell: ({ row }) => (
<DataTableConfidence
value={row.original.confidence}
showPercent={row.original.confidence > 70}
/>
),
},
{
accessorKey: "cost",
size: 110,
header: "Cost",
cell: ({ row }) => (
<span className="font-medium text-foreground/85">
{row.original.cost}
</span>
),
},
{
accessorKey: "tasks",
size: 100,
header: "Tasks",
cell: ({ row }) => <DataTableTaskCount count={row.original.tasks} />,
},
{
accessorKey: "assignee",
size: 150,
header: "Assignee",
cell: ({ row }) => <DataTableAvatarStack users={row.original.assignee} />,
},
];
export function DataTableDemo() {
return (
<div className="w-full max-w-4xl">
<DataTable data={agents} columns={columns} pageSize={6} totalItems={35} />
</div>
);
}
pnpm dlx shadcn@latest add https://matos-ui.com/r/data-table.jsonimport type { ColumnDef } from "@tanstack/react-table"
import {
DataTable,
DataTableBadge,
DataTableColumnHeader
} from "@/components/matos-ui/data-table"
import { BriefcaseBusiness, Building2, Hash, Mail } from "lucide-react"type Employee = {
id: string
department: "Finance" | "HR" | "Marketing" | "Sales" | "Engineering"
email: string
employment: "Active" | "Inactive"
firstName: string
lastName: string
}
const columns: ColumnDef<Employee>[] = [
{
accessorKey: "id",
header: ({ column }) => (
<DataTableColumnHeader
column={column}
title="ID"
icon={<Hash aria-hidden="true" />}
/>
)
},
{
accessorKey: "department",
header: ({ column }) => (
<DataTableColumnHeader
column={column}
title="Department"
icon={<Building2 aria-hidden="true" />}
/>
),
cell: ({ row }) => (
<DataTableBadge tone="engineering">
{row.getValue("department")}
</DataTableBadge>
)
},
{
accessorKey: "email",
header: ({ column }) => (
<DataTableColumnHeader
column={column}
title="Email"
icon={<Mail aria-hidden="true" />}
/>
)
},
{
accessorKey: "employment",
header: ({ column }) => (
<DataTableColumnHeader
column={column}
title="Employment"
icon={<BriefcaseBusiness aria-hidden="true" />}
/>
)
}
]<DataTable
data={employees}
columns={columns}
/>| Prop | Type | Default | Description |
|---|---|---|---|
data | TData[] | - | Records rendered by TanStack Table. |
columns | ColumnDef<TData, TValue>[] | - | Column definitions passed to TanStack Table. |
title | string? | - | Loose header title. |
description | string? | - | Loose header description. |
searchKey | string? | - | Column id/accessor key used by the search input. |
searchPlaceholder | string? | "Search records..." | Placeholder for the search input. |
pageSize | number? | 10 | Initial page size. |
pageSizeOptions | number[]? | [5, 10, 20, 50] | Options shown in the rows-per-page select. |
loading | boolean? | false | Shows table skeleton rows. |
emptyTitle | string? | "No records found" | Empty state title. |
emptyDescription | string? | "Try adjusting..." | Empty state description. |
toolbarActions | ReactNode? | - | Optional actions rendered beside search. |
getRowState | (row: TData) => DataTableRowState | undefined | Inferred from row fields | Controls inactive/deleted row visuals. |
DataTable can infer row state from deleted, disabled, inactive, status, or employment fields. You can override this with getRowState.
| State | Visual treatment |
|---|---|
"default" | Normal row with subtle hover. |
"inactive" | Reduced opacity and muted text. |
"deleted" | Reduced opacity plus a soft diagonal stripe fill. |
Also exported: DataTableToolbar, DataTablePagination, DataTableEmpty, DataTableBadge, DataTableColumnHeader, DataTableRowState, and the semantic table primitives Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, and TableCaption.
Install Matos UI
Choose a package manager and copy one command for every component.