Data Table

Present structured data in sortable tables.
Table data shown as expandable cards. Each card represents one row. Columns: Symbol, Company, Price, Change, Change %, Volume.

<DataTable  columns={[      {          "key": "symbol",          "label": "Symbol",          "priority": "primary"      },      {          "key": "name",          "label": "Company",          "priority": "primary"      },      {          "key": "price",          "label": "Price",          "align": "right",          "priority": "primary",          "format": {              "kind": "currency",              "currency": "USD",              "decimals": 2          }      },      {          "key": "change",          "label": "Change",          "align": "right",          "priority": "secondary",          "format": {              "kind": "delta",              "decimals": 2,              "upIsPositive": true,              "showSign": true          }      },      {          "key": "changePercent",          "label": "Change %",          "align": "right",          "priority": "secondary",          "format": {              "kind": "percent",              "decimals": 2,              "showSign": true,              "basis": "unit"          }      },      {          "key": "volume",          "label": "Volume",          "align": "right",          "priority": "secondary",          "format": {              "kind": "number",              "compact": true          }      }  ]}  data={[      {          "symbol": "IBM",          "name": "International Business Machines",          "price": 170.42,          "change": 1.12,          "changePercent": 0.66,          "volume": 18420000      },      {          "symbol": "AAPL",          "name": "Apple",          "price": 178.25,          "change": 2.35,          "changePercent": 1.34,          "volume": 52430000      },      {          "symbol": "MSFT",          "name": "Microsoft",          "price": 380,          "change": 1.24,          "changePercent": 0.33,          "volume": 31250000      },      {          "symbol": "INTC",          "name": "Intel Corporation",          "price": 39.85,          "change": -0.42,          "changePercent": -1.04,          "volume": 29840000      },      {          "symbol": "ORCL",          "name": "Oracle Corporation",          "price": 110.31,          "change": 0.78,          "changePercent": 0.71,          "volume": 14230000      }  ]}  rowIdKey="symbol"/>

Search results, issue lists, transaction logs: the assistant surfaces tabular data constantly. DataTable renders those rows as a sortable table on desktop and stacked cards on mobile. You define columns with declarative formatting configs for currency, dates, status badges, and links. No raw JSON, no custom table from scratch.

Role: Information. For displaying data the user reads. See Design Guidelines for how component roles work.

Getting Started

Run this once from your project root.

npx shadcn@latest add @tool-ui/data-table

Render DataTable in your UI with tool-compatible props.

import { DataTable, type Column } from "@/components/tool-ui/data-table";type Row = {  id: string;  issue: string;  priority: "high" | "medium" | "low";  createdAt: string;  amount: number;};const columns: Column<Row>[] = [  { key: "issue", label: "Issue", priority: "primary", truncate: true },  {    key: "priority",    label: "Urgency",    format: {      kind: "status",      statusMap: {        high: { tone: "danger", label: "High" },        medium: { tone: "warning", label: "Medium" },        low: { tone: "neutral", label: "Low" },      },    },  },  {    key: "createdAt",    label: "Created",    format: { kind: "date", dateFormat: "relative" },  },  {    key: "amount",    label: "Amount",    align: "right",    format: { kind: "currency", currency: "USD", decimals: 2 },  },];const data: Row[] = [  {    id: "1",    issue: "Payment failing on checkout",    priority: "high",    createdAt: "2025-11-11T09:24:00Z",    amount: 129.99,  },  {    id: "2",    issue: "Billing UI alignment bug",    priority: "medium",    createdAt: "2025-11-10T17:03:00Z",    amount: 42,  },  {    id: "3",    issue: "Export CSV missing headers",    priority: "low",    createdAt: "2025-11-08T08:00:00Z",    amount: 0,  },  {    id: "4",    issue: "Webhook retries inconsistent",    priority: "high",    createdAt: "2025-11-13T22:15:00Z",    amount: 527.5,  },];export default function Example() {  return (    <DataTable      rowIdKey="id"      columns={columns}      data={data}      defaultSort={{ by: "createdAt", direction: "desc" }}    />  );}

Register this renderer so tool results display as DataTable.

// Backend toolimport { tool, jsonSchema } from "ai";const searchProducts = tool({  description: "Search products and return results in a table",  inputSchema: jsonSchema<{ query: string }>({    type: "object",    properties: { query: { type: "string" } },    required: ["query"],    additionalProperties: false,  }),  async execute({ query }) {    const results = await db.products.search(query);    return {      id: "data-table-search-products",      columns: [        { key: "name", label: "Product", priority: "primary" },        {          key: "price",          label: "Price",          format: { kind: "currency", currency: "USD" },        },        { key: "stock", label: "Stock", align: "right" },      ],      data: results.map((p) => ({        id: p.id,        name: p.name,        price: p.price,        stock: p.stock,      })),    };  },});// Frontend with assistant-uiimport { type Toolkit } from "@assistant-ui/react";import { DataTable } from "@/components/tool-ui/data-table";import { safeParseSerializableDataTable } from "@/components/tool-ui/data-table/schema";import { createResultToolRenderer } from "@/components/tool-ui/shared";export const toolkit: Toolkit = {  searchProducts: {    type: "backend",    render: createResultToolRenderer({      safeParse: safeParseSerializableDataTable,      render: (parsedResult) => (        <>          <DataTable rowIdKey="id" {...parsedResult} />        </>      ),    }),  },};

Key Features

Responsive layout

Table on desktop, stacked cards on mobile

Sorting

Tri-state column headers: none, ascending, descending

Declarative formatting

Currency, dates, links, badges, and status pills via column config

External actions

Compose action buttons below the table

Layout Variants

Use explicit components when you want to force a layout:

  • <DataTable> (default): responsive auto layout
  • <DataTable.Table>: always render the table view
  • <DataTable.Cards>: always render the card view
import { DataTable } from "@/components/tool-ui/data-table";

Props

Core Props

Prop

Type

Column Options

Prop

Type

Sorting

Prop

Type

External Actions

Use ToolUI.LocalActions to compose surface-level actions below DataTable.

Formatting

All visual formatting for data stays declarative via columns[i].format. Keep row values as primitives and describe presentation through format configs.

Prop

Type

Accessibility

  • Uses semantic <table> markup with proper header associations
  • Sortable columns are keyboard-accessible via shadcn/ui Button
  • Sort changes are announced via aria-live for assistive technologies