# Data Table Artifact

Data Table Artifact [#data-table-artifact]

The **Data Table Artifact** is a docked-canvas, sortable data table for any **local** row data: VectorDB search results, a model catalog, evaluation rows, or the array output of [`generateObject()`](https://localmode.dev/docs/react). Click a column header to sort the rows ascending → descending → unsorted — all computed in-browser, never on a server.

It is distinct from the inline `ScoredResultBarList` (a message-stream primitive): this is a *docked canvas* table you place beside the chat.

**Local content only.** No server, no sandbox. Sorting reorders a copy of your rows client-side; the input array is untouched.

**When to use it:** render a structured result set the user can scan and re-sort — e.g. a model picker, retrieval results, or an extracted-records table from a `generateObject()` call.

Preview [#preview]

```tsx
'use client';

/**
 * @file data-table-artifact-demo.tsx
 * @description Demo for DataTableArtifact, used by the docs live preview. Renders
 * local model-catalog rows (no model download needed) and lets you click a
 * column header to sort entirely client-side.
 */

import { DataTableArtifact } from '@/components/data-table-artifact';

interface ModelRow extends Record<string, unknown> {
  model: string;
  params: string;
  sizeMB: number;
  contextK: number;
}

const ROWS: ModelRow[] = [
  { model: 'SmolLM2 135M', params: '135M', sizeMB: 92, contextK: 8 },
  { model: 'SmolLM2 360M', params: '360M', sizeMB: 230, contextK: 8 },
  { model: 'Qwen2.5 0.5B', params: '500M', sizeMB: 398, contextK: 32 },
  { model: 'Llama 3.2 1B', params: '1B', sizeMB: 808, contextK: 128 },
  { model: 'Gemma 2 2B', params: '2B', sizeMB: 1640, contextK: 8 },
];

export default function DataTableArtifactDemo() {
  return (
    <DataTableArtifact<ModelRow>
      rows={ROWS}
      caption="Click a column header to sort - runs entirely in your browser."
      columns={[
        { key: 'model', header: 'Model' },
        { key: 'params', header: 'Params' },
        { key: 'sizeMB', header: 'Size (MB)', align: 'right' },
        { key: 'contextK', header: 'Context (K)', align: 'right' },
      ]}
      className="w-full min-w-0 max-w-lg"
    />
  );
}
```

Installation [#installation]

```bash
npx shadcn@latest add @localmode/ui/artifacts/data-table-artifact
```

Dependencies [#dependencies]

* **Data source:** renders the rows you pass and sorts them client-side — works with any backend (any in-memory array). Recommended LocalMode producers: `useGenerateObject()` from `@localmode/react` or VectorDB search results (optional).
* `lucide-react` — sort-direction icons
* `ui/table` — the shadcn/ui table primitive (installed automatically as a registry dependency)
* `clsx` + `tailwind-merge` — via the shared `cn()` util (installed automatically as a registry dependency)

Files installed [#files-installed]

* `data-table-artifact.tsx` — the component (uses the `ui/table` primitive)
* `ui/table.tsx` — the shadcn/ui table primitive (registry dependency)
* `lib/utils.ts` — the `cn()` helper (if not already present)

Props [#props]

**DataTableArtifact**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `rows` | `array` | — | **Required.** The rows to render. Sorting reorders a copy; the input array is untouched. |
| `columns` | `array` | — | Column definitions. When omitted, columns are inferred from the keys of the first row. |
| `caption` | `ReactNode` | — | Optional caption rendered under the table. |
| `initialSortKey` | `string & keyof Row` | — | Initial sort column key. |
| `initialSortDirection` | `"desc" \| "asc"` | `"asc"` | Initial sort direction. |
| `emptyMessage` | `ReactNode` | `"No data"` | Message shown when `rows` is empty. |

DataTableColumn [#datatablecolumn]

**DataTableColumn**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `key` | `string & keyof Row` | — | **Required.** Key into the row object (used as the default accessor and sort key). |
| `header` | `string` | — | Header label. Defaults to `key`. |
| `cell` | `function` | — | Custom cell renderer. Defaults to `String(row[key])`. |
| `sortable` | `boolean` | `true` | When false, the column header is not clickable to sort. |
| `align` | `"center" \| "right" \| "left"` | `"left"` | Align the column's text. |

Examples [#examples]

From a `generateObject()` array [#from-a-generateobject-array]

```tsx
import { useGenerateObject, jsonSchema } from '@localmode/react';
import { z } from 'zod';
import { DataTableArtifact } from '@/components/artifacts/data-table-artifact';

const schema = jsonSchema(
  z.object({ rows: z.array(z.object({ name: z.string(), score: z.number() })) }),
);

export function ExtractedTable({ model }) {
  const { data, execute } = useGenerateObject({ model, schema });
  const rows = data?.object.rows ?? [];

  return (
    <DataTableArtifact
      rows={rows}
      columns={[
        { key: 'name', header: 'Name' },
        { key: 'score', header: 'Score', align: 'right' },
      ]}
    />
  );
}
```

From VectorDB search results [#from-vectordb-search-results]

```tsx
<DataTableArtifact
  rows={results.map((r) => ({ id: r.id, score: r.score, title: r.metadata.title }))}
  initialSortKey="score"
  initialSortDirection="desc"
  columns={[
    { key: 'title', header: 'Title' },
    { key: 'score', header: 'Similarity', align: 'right', cell: (r) => r.score.toFixed(3) },
    { key: 'id', header: 'ID' },
  ]}
/>
```

Inferred columns [#inferred-columns]

Omit `columns` and the table infers them from the first row's keys:

```tsx
<DataTableArtifact rows={rows} />
```

Sorting [#sorting]

Clicking a sortable header cycles **ascending → descending → unsorted**. Numbers, strings (natural/numeric-aware), booleans, and `Date` values are compared correctly; `null`/`undefined` sort first. Set `sortable: false` on a column to disable it, and `initialSortKey` / `initialSortDirection` to start pre-sorted.

Local-first boundary [#local-first-boundary]

This is a **local** table. It renders data your app already has in memory (VectorDB results, model catalogs, `generateObject()` output) and sorts it client-side. It does not fetch from a server or run remote queries.

Customization [#customization]

Styled with shadcn/ui CSS-variable utilities via the `ui/table` primitive, so it inherits your theme. Because you own the file, provide a custom `cell` renderer per column (badges, links, formatted numbers), adjust alignment, or wrap it inside an [`Artifact`](/docs/artifacts/artifact) canvas.