---
title: DataTable
description: High-level data table with sorting, filtering, and pagination.
icon: table-cells
---

import { ComponentPreview } from '/snippets/component-preview.mdx'

DataTable wraps tanstack/react-table with a flat Python API. Hand it columns and rows, and it handles sorting, filtering, pagination, and formatting — no manual `<TableRow>` assembly required. For static display tables without interactivity, use [Table](/components/table) instead.

## Basic Usage

<ComponentPreview json={{"view":{"type":"DataTable","columns":[{"key":"name","header":"Name","sortable":true},{"key":"email","header":"Email","sortable":false},{"key":"role","header":"Role","sortable":true}],"rows":[{"name":"Alice Johnson","email":"alice@example.com","role":"Admin"},{"name":"Bob Smith","email":"bob@example.com","role":"Editor"},{"name":"Carol White","email":"carol@example.com","role":"Viewer"}],"search":false,"paginated":false,"pageSize":10}}} playground=" ">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import DataTable, DataTableColumn

DataTable(
    columns=[
        DataTableColumn(key="name", header="Name", sortable=True),
        DataTableColumn(key="email", header="Email"),
        DataTableColumn(key="role", header="Role", sortable=True),
    ],
    rows=[
        {"name": "Alice Johnson", "email": "alice@example.com", "role": "Admin"},
        {"name": "Bob Smith", "email": "bob@example.com", "role": "Editor"},
        {"name": "Carol White", "email": "carol@example.com", "role": "Viewer"},
    ],
)
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "type": "DataTable",
    "columns": [
      {"key": "name", "header": "Name", "sortable": true},
      {"key": "email", "header": "Email", "sortable": false},
      {"key": "role", "header": "Role", "sortable": true}
    ],
    "rows": [
      {"name": "Alice Johnson", "email": "alice@example.com", "role": "Admin"},
      {"name": "Bob Smith", "email": "bob@example.com", "role": "Editor"},
      {"name": "Carol White", "email": "carol@example.com", "role": "Viewer"}
    ],
    "search": false,
    "paginated": false,
    "pageSize": 10
  }
}
```
</CodeGroup>
</ComponentPreview>

## Pagination

For large datasets, `paginated=True` shows page controls and displays `page_size` rows at a time. Sorting and filtering still work across the full dataset — pagination just controls how many rows are visible.

<ComponentPreview json={{"view":{"type":"DataTable","columns":[{"key":"id","header":"ID","sortable":false},{"key":"task","header":"Task","sortable":true},{"key":"status","header":"Status","sortable":false}],"rows":[{"id":"T-001","task":"Design system","status":"Complete"},{"id":"T-002","task":"API integration","status":"In Progress"},{"id":"T-003","task":"Testing","status":"Pending"},{"id":"T-004","task":"Documentation","status":"In Progress"},{"id":"T-005","task":"Deployment","status":"Pending"}],"search":false,"paginated":true,"pageSize":3}}} playground=" ">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import DataTable, DataTableColumn

DataTable(
    columns=[
        DataTableColumn(key="id", header="ID"),
        DataTableColumn(key="task", header="Task", sortable=True),
        DataTableColumn(key="status", header="Status"),
    ],
    rows=[
        {"id": "T-001", "task": "Design system", "status": "Complete"},
        {"id": "T-002", "task": "API integration", "status": "In Progress"},
        {"id": "T-003", "task": "Testing", "status": "Pending"},
        {"id": "T-004", "task": "Documentation", "status": "In Progress"},
        {"id": "T-005", "task": "Deployment", "status": "Pending"},
    ],
    paginated=True,
    page_size=3,
)
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "type": "DataTable",
    "columns": [
      {"key": "id", "header": "ID", "sortable": false},
      {"key": "task", "header": "Task", "sortable": true},
      {"key": "status", "header": "Status", "sortable": false}
    ],
    "rows": [
      {"id": "T-001", "task": "Design system", "status": "Complete"},
      {"id": "T-002", "task": "API integration", "status": "In Progress"},
      {"id": "T-003", "task": "Testing", "status": "Pending"},
      {"id": "T-004", "task": "Documentation", "status": "In Progress"},
      {"id": "T-005", "task": "Deployment", "status": "Pending"}
    ],
    "search": false,
    "paginated": true,
    "pageSize": 3
  }
}
```
</CodeGroup>
</ComponentPreview>

## Search

Set `search=True` to show a text input above the table that narrows visible rows across all columns. Search works alongside sorting and pagination — the search applies first, then sort order, then page boundaries.

<ComponentPreview json={{"view":{"type":"DataTable","columns":[{"key":"name","header":"Name","sortable":true},{"key":"species","header":"Species","sortable":false},{"key":"status","header":"Status","sortable":false}],"rows":[{"name":"Arthur Dent","species":"Human","status":"Confused"},{"name":"Ford Prefect","species":"Betelgeusian","status":"Drinking"},{"name":"Zaphod","species":"Betelgeusian","status":"Presidential"},{"name":"Trillian","species":"Human","status":"Navigating"},{"name":"Marvin","species":"Android","status":"Depressed"}],"search":true,"paginated":false,"pageSize":10}}} playground=" ">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import DataTable, DataTableColumn

DataTable(
    columns=[
        DataTableColumn(key="name", header="Name", sortable=True),
        DataTableColumn(key="species", header="Species"),
        DataTableColumn(key="status", header="Status"),
    ],
    rows=[
        {"name": "Arthur Dent", "species": "Human", "status": "Confused"},
        {"name": "Ford Prefect", "species": "Betelgeusian", "status": "Drinking"},
        {"name": "Zaphod", "species": "Betelgeusian", "status": "Presidential"},
        {"name": "Trillian", "species": "Human", "status": "Navigating"},
        {"name": "Marvin", "species": "Android", "status": "Depressed"},
    ],
    search=True,
)
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "type": "DataTable",
    "columns": [
      {"key": "name", "header": "Name", "sortable": true},
      {"key": "species", "header": "Species", "sortable": false},
      {"key": "status", "header": "Status", "sortable": false}
    ],
    "rows": [
      {"name": "Arthur Dent", "species": "Human", "status": "Confused"},
      {"name": "Ford Prefect", "species": "Betelgeusian", "status": "Drinking"},
      {"name": "Zaphod", "species": "Betelgeusian", "status": "Presidential"},
      {"name": "Trillian", "species": "Human", "status": "Navigating"},
      {"name": "Marvin", "species": "Android", "status": "Depressed"}
    ],
    "search": true,
    "paginated": false,
    "pageSize": 10
  }
}
```
</CodeGroup>
</ComponentPreview>

## Columns

`DataTableColumn` defines everything about a column: what data to show, how to size it, and how to style it.

### Widths

Set `width` for fixed sizing, or `min_width`/`max_width` for flexible constraints. Without widths, columns auto-size to content.

<ComponentPreview json={{"view":{"type":"DataTable","columns":[{"key":"id","header":"ID","sortable":false,"width":"80px"},{"key":"name","header":"Name","sortable":false,"minWidth":"150px","maxWidth":"300px"},{"key":"amount","header":"Amount","sortable":false,"width":"120px","headerClass":"text-right","cellClass":"text-right"}],"rows":[{"id":"001","name":"Arthur Dent","amount":"$1,200.00"},{"id":"002","name":"Ford Prefect","amount":"$850.50"},{"id":"003","name":"Trillian","amount":"$2,100.00"}],"search":false,"paginated":false,"pageSize":10}}} playground=" ">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import DataTable, DataTableColumn

DataTable(
    columns=[
        DataTableColumn(key="id", header="ID", width="80px"),
        DataTableColumn(
            key="name", header="Name",
            min_width="150px", max_width="300px",
        ),
        DataTableColumn(
            key="amount", header="Amount",
            width="120px", align="right",
        ),
    ],
    rows=[
        {"id": "001", "name": "Arthur Dent", "amount": "$1,200.00"},
        {"id": "002", "name": "Ford Prefect", "amount": "$850.50"},
        {"id": "003", "name": "Trillian", "amount": "$2,100.00"},
    ],
)
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "type": "DataTable",
    "columns": [
      {"key": "id", "header": "ID", "sortable": false, "width": "80px"},
      {
        "key": "name",
        "header": "Name",
        "sortable": false,
        "minWidth": "150px",
        "maxWidth": "300px"
      },
      {
        "key": "amount",
        "header": "Amount",
        "sortable": false,
        "width": "120px",
        "headerClass": "text-right",
        "cellClass": "text-right"
      }
    ],
    "rows": [
      {"id": "001", "name": "Arthur Dent", "amount": "$1,200.00"},
      {"id": "002", "name": "Ford Prefect", "amount": "$850.50"},
      {"id": "003", "name": "Trillian", "amount": "$2,100.00"}
    ],
    "search": false,
    "paginated": false,
    "pageSize": 10
  }
}
```
</CodeGroup>
</ComponentPreview>

### Alignment

Right-align numbers so decimal points line up. Center short values like region codes. `align` applies to both the header and cells — it compiles to `header_class` and `cell_class` Python-side, so the protocol only sees CSS classes.

<ComponentPreview json={{"view":{"type":"DataTable","columns":[{"key":"name","header":"Name","sortable":false},{"key":"region","header":"Region","sortable":false,"headerClass":"text-center","cellClass":"text-center"},{"key":"revenue","header":"Revenue","sortable":false,"headerClass":"text-right","cellClass":"text-right"},{"key":"growth","header":"Growth","sortable":false,"headerClass":"text-right","cellClass":"text-right"}],"rows":[{"name":"Milliways Inc","region":"Galaxy","revenue":"$4.2M","growth":"+12%"},{"name":"Sirius Corp","region":"Sector ZZ9","revenue":"$2.8M","growth":"-3%"},{"name":"Magrathea Ltd","region":"Horsehead","revenue":"$1.5M","growth":"+28%"}],"search":false,"paginated":false,"pageSize":10}}} playground=" ">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import DataTable, DataTableColumn

DataTable(
    columns=[
        DataTableColumn(key="name", header="Name"),
        DataTableColumn(key="region", header="Region", align="center"),
        DataTableColumn(key="revenue", header="Revenue", align="right"),
        DataTableColumn(key="growth", header="Growth", align="right"),
    ],
    rows=[
        {"name": "Milliways Inc", "region": "Galaxy", "revenue": "$4.2M", "growth": "+12%"},
        {"name": "Sirius Corp", "region": "Sector ZZ9", "revenue": "$2.8M", "growth": "-3%"},
        {"name": "Magrathea Ltd", "region": "Horsehead", "revenue": "$1.5M", "growth": "+28%"},
    ],
)
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "type": "DataTable",
    "columns": [
      {"key": "name", "header": "Name", "sortable": false},
      {
        "key": "region",
        "header": "Region",
        "sortable": false,
        "headerClass": "text-center",
        "cellClass": "text-center"
      },
      {
        "key": "revenue",
        "header": "Revenue",
        "sortable": false,
        "headerClass": "text-right",
        "cellClass": "text-right"
      },
      {
        "key": "growth",
        "header": "Growth",
        "sortable": false,
        "headerClass": "text-right",
        "cellClass": "text-right"
      }
    ],
    "rows": [
      {
        "name": "Milliways Inc",
        "region": "Galaxy",
        "revenue": "$4.2M",
        "growth": "+12%"
      },
      {
        "name": "Sirius Corp",
        "region": "Sector ZZ9",
        "revenue": "$2.8M",
        "growth": "-3%"
      },
      {
        "name": "Magrathea Ltd",
        "region": "Horsehead",
        "revenue": "$1.5M",
        "growth": "+28%"
      }
    ],
    "search": false,
    "paginated": false,
    "pageSize": 10
  }
}
```
</CodeGroup>
</ComponentPreview>

### Styling

`header_class` and `cell_class` apply arbitrary Tailwind to individual columns — font weights, colors, monospace formatting, truncation. These are the escape hatches when `align` and `width` aren't enough.

<ComponentPreview json={{"view":{"type":"DataTable","columns":[{"key":"name","header":"Name","sortable":false,"headerClass":"text-emerald-600","cellClass":"font-semibold text-emerald-600"},{"key":"email","header":"Email","sortable":false,"cellClass":"text-muted-foreground"},{"key":"amount","header":"Amount","sortable":false,"headerClass":"text-right","cellClass":"font-mono text-right"}],"rows":[{"name":"Arthur Dent","email":"arthur@earth.com","amount":"$1,200.00"},{"name":"Ford Prefect","email":"ford@betelgeuse.com","amount":"$850.50"},{"name":"Trillian","email":"trillian@hog.com","amount":"$2,100.00"}],"search":false,"paginated":false,"pageSize":10}}} playground=" ">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import DataTable, DataTableColumn

DataTable(
    columns=[
        DataTableColumn(
            key="name", header="Name",
            header_class="text-emerald-600",
            cell_class="font-semibold text-emerald-600",
        ),
        DataTableColumn(
            key="email", header="Email",
            cell_class="text-muted-foreground",
        ),
        DataTableColumn(
            key="amount", header="Amount",
            align="right", cell_class="font-mono",
        ),
    ],
    rows=[
        {"name": "Arthur Dent", "email": "arthur@earth.com", "amount": "$1,200.00"},
        {"name": "Ford Prefect", "email": "ford@betelgeuse.com", "amount": "$850.50"},
        {"name": "Trillian", "email": "trillian@hog.com", "amount": "$2,100.00"},
    ],
)
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "type": "DataTable",
    "columns": [
      {
        "key": "name",
        "header": "Name",
        "sortable": false,
        "headerClass": "text-emerald-600",
        "cellClass": "font-semibold text-emerald-600"
      },
      {
        "key": "email",
        "header": "Email",
        "sortable": false,
        "cellClass": "text-muted-foreground"
      },
      {
        "key": "amount",
        "header": "Amount",
        "sortable": false,
        "headerClass": "text-right",
        "cellClass": "font-mono text-right"
      }
    ],
    "rows": [
      {"name": "Arthur Dent", "email": "arthur@earth.com", "amount": "$1,200.00"},
      {"name": "Ford Prefect", "email": "ford@betelgeuse.com", "amount": "$850.50"},
      {"name": "Trillian", "email": "trillian@hog.com", "amount": "$2,100.00"}
    ],
    "search": false,
    "paginated": false,
    "pageSize": 10
  }
}
```
</CodeGroup>
</ComponentPreview>

## Cells

### Formatting

The `format` field applies an [expression pipe](/expressions/pipes) to every value in a column. Any pipe that works in `{{ value | pipe }}` expressions works here — `currency`, `percent:1`, `number:2`, `date:long`, and any future pipes you add.

<ComponentPreview json={{"view":{"type":"DataTable","columns":[{"key":"product","header":"Product","sortable":false},{"key":"revenue","header":"Revenue","sortable":true,"format":"currency"},{"key":"growth","header":"Growth","sortable":true,"format":"percent:1"},{"key":"units","header":"Units","sortable":true,"format":"number"}],"rows":[{"product":"Widget Pro","revenue":128450,"growth":0.142,"units":3210},{"product":"Gadget Plus","revenue":84200,"growth":0.087,"units":1980},{"product":"Doohickey","revenue":52100,"growth":-0.031,"units":870}],"search":false,"paginated":false,"pageSize":10}}} playground=" ">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import DataTable, DataTableColumn

DataTable(
    columns=[
        DataTableColumn(key="product", header="Product"),
        DataTableColumn(
            key="revenue", header="Revenue",
            sortable=True, format="currency",
        ),
        DataTableColumn(
            key="growth", header="Growth",
            sortable=True, format="percent:1",
        ),
        DataTableColumn(
            key="units", header="Units",
            sortable=True, format="number",
        ),
    ],
    rows=[
        {"product": "Widget Pro", "revenue": 128450, "growth": 0.142, "units": 3210},
        {"product": "Gadget Plus", "revenue": 84200, "growth": 0.087, "units": 1980},
        {"product": "Doohickey", "revenue": 52100, "growth": -0.031, "units": 870},
    ],
)
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "type": "DataTable",
    "columns": [
      {"key": "product", "header": "Product", "sortable": false},
      {"key": "revenue", "header": "Revenue", "sortable": true, "format": "currency"},
      {"key": "growth", "header": "Growth", "sortable": true, "format": "percent:1"},
      {"key": "units", "header": "Units", "sortable": true, "format": "number"}
    ],
    "rows": [
      {"product": "Widget Pro", "revenue": 128450, "growth": 0.142, "units": 3210},
      {"product": "Gadget Plus", "revenue": 84200, "growth": 0.087, "units": 1980},
      {"product": "Doohickey", "revenue": 52100, "growth": -0.031, "units": 870}
    ],
    "search": false,
    "paginated": false,
    "pageSize": 10
  }
}
```
</CodeGroup>
</ComponentPreview>

See [Pipes](/expressions/pipes) for the full list of available format pipes.

### Components

Cell values can be full Prefab components — Badges, Sparklines, Progress bars, anything. Put a component instance directly in the row dict and it renders inline.

<ComponentPreview json={{"view":{"type":"DataTable","columns":[{"key":"metric","header":"Metric","sortable":true},{"key":"trend","header":"Trend","sortable":false},{"key":"status","header":"Status","sortable":false}],"rows":[{"metric":"Revenue","trend":{"cssClass":"w-24","type":"Sparkline","data":[45,52,49,60,55,72,68,75],"variant":"success","fill":true,"curve":"linear","strokeWidth":1.5},"status":{"type":"Badge","label":"Growing","variant":"success"}},{"metric":"Churn","trend":{"cssClass":"w-24","type":"Sparkline","data":[12,15,18,14,20,22,19,25],"variant":"destructive","fill":false,"curve":"linear","strokeWidth":1.5},"status":{"type":"Badge","label":"At risk","variant":"destructive"}},{"metric":"Users","trend":{"cssClass":"w-24","type":"Sparkline","data":[250,248,252,249,253,251,250,252],"variant":"info","fill":true,"curve":"linear","strokeWidth":1.5},"status":{"type":"Badge","label":"Stable","variant":"secondary"}}],"search":false,"paginated":false,"pageSize":10}}} playground=" ">
<CodeGroup>
```python Python expandable icon="python"
from prefab_ui.components import DataTable, DataTableColumn, Badge
from prefab_ui.components.charts import Sparkline

DataTable(
    columns=[
        DataTableColumn(key="metric", header="Metric", sortable=True),
        DataTableColumn(key="trend", header="Trend"),
        DataTableColumn(key="status", header="Status"),
    ],
    rows=[
        {
            "metric": "Revenue",
            "trend": Sparkline(
                data=[45, 52, 49, 60, 55, 72, 68, 75],
                variant="success", fill=True, css_class="w-24",
            ),
            "status": Badge("Growing", variant="success"),
        },
        {
            "metric": "Churn",
            "trend": Sparkline(
                data=[12, 15, 18, 14, 20, 22, 19, 25],
                variant="destructive", css_class="w-24",
            ),
            "status": Badge("At risk", variant="destructive"),
        },
        {
            "metric": "Users",
            "trend": Sparkline(
                data=[250, 248, 252, 249, 253, 251, 250, 252],
                variant="info", fill=True, css_class="w-24",
            ),
            "status": Badge("Stable", variant="secondary"),
        },
    ],
)
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "type": "DataTable",
    "columns": [
      {"key": "metric", "header": "Metric", "sortable": true},
      {"key": "trend", "header": "Trend", "sortable": false},
      {"key": "status", "header": "Status", "sortable": false}
    ],
    "rows": [
      {
        "metric": "Revenue",
        "trend": {
          "cssClass": "w-24",
          "type": "Sparkline",
          "data": [45, 52, 49, 60, 55, 72, 68, 75],
          "variant": "success",
          "fill": true,
          "curve": "linear",
          "strokeWidth": 1.5
        },
        "status": {"type": "Badge", "label": "Growing", "variant": "success"}
      },
      {
        "metric": "Churn",
        "trend": {
          "cssClass": "w-24",
          "type": "Sparkline",
          "data": [12, 15, 18, 14, 20, 22, 19, 25],
          "variant": "destructive",
          "fill": false,
          "curve": "linear",
          "strokeWidth": 1.5
        },
        "status": {"type": "Badge", "label": "At risk", "variant": "destructive"}
      },
      {
        "metric": "Users",
        "trend": {
          "cssClass": "w-24",
          "type": "Sparkline",
          "data": [250, 248, 252, 249, 253, 251, 250, 252],
          "variant": "info",
          "fill": true,
          "curve": "linear",
          "strokeWidth": 1.5
        },
        "status": {"type": "Badge", "label": "Stable", "variant": "secondary"}
      }
    ],
    "search": false,
    "paginated": false,
    "pageSize": 10
  }
}
```
</CodeGroup>
</ComponentPreview>

## Row Click

Make rows clickable with `on_row_click`. The clicked row highlights and the action fires with the row's data as `$event`. Use `EVENT` from `prefab_ui.rx` for a cleaner syntax than raw template strings.

<ComponentPreview json={{"view":{"type":"DataTable","columns":[{"key":"name","header":"Name","sortable":true},{"key":"email","header":"Email","sortable":false},{"key":"role","header":"Role","sortable":false}],"rows":[{"name":"Arthur Dent","email":"arthur@earth.com","role":"Admin"},{"name":"Ford Prefect","email":"ford@betelgeuse.com","role":"Editor"},{"name":"Trillian","email":"trillian@hog.com","role":"Viewer"}],"search":false,"paginated":false,"pageSize":10,"onRowClick":{"action":"showToast","message":"{{ $event.name }} ({{ $event.email }})"}}}} playground=" ">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import DataTable, DataTableColumn, EVENT
from prefab_ui.actions.ui import ShowToast

DataTable(
    columns=[
        DataTableColumn(key="name", header="Name", sortable=True),
        DataTableColumn(key="email", header="Email"),
        DataTableColumn(key="role", header="Role"),
    ],
    rows=[
        {"name": "Arthur Dent", "email": "arthur@earth.com", "role": "Admin"},
        {"name": "Ford Prefect", "email": "ford@betelgeuse.com", "role": "Editor"},
        {"name": "Trillian", "email": "trillian@hog.com", "role": "Viewer"},
    ],
    on_row_click=ShowToast(f"{EVENT.name} ({EVENT.email})"),
)
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "type": "DataTable",
    "columns": [
      {"key": "name", "header": "Name", "sortable": true},
      {"key": "email", "header": "Email", "sortable": false},
      {"key": "role", "header": "Role", "sortable": false}
    ],
    "rows": [
      {"name": "Arthur Dent", "email": "arthur@earth.com", "role": "Admin"},
      {"name": "Ford Prefect", "email": "ford@betelgeuse.com", "role": "Editor"},
      {"name": "Trillian", "email": "trillian@hog.com", "role": "Viewer"}
    ],
    "search": false,
    "paginated": false,
    "pageSize": 10,
    "onRowClick": {"action": "showToast", "message": "{{ $event.name }} ({{ $event.email }})"}
  }
}
```
</CodeGroup>
</ComponentPreview>

## From DataFrame

Pass a pandas or polars DataFrame directly as `rows`. Columns auto-generate from the DataFrame's column names — each column in the DataFrame becomes a `DataTableColumn` with `key` and `header` set to the column name.

When you pass explicit `columns`, the `key` on each column must match a DataFrame column name — that's how the table knows which data goes where. This lets you control headers, sorting, formatting, and alignment while the DataFrame provides the raw data.

```python
import pandas as pd
from prefab_ui.components import DataTable, DataTableColumn

df = pd.DataFrame({
    "name": ["Arthur Dent", "Ford Prefect", "Trillian"],
    "score": [42, 87, 99],
    "region": ["Earth", "Betelgeuse", "Earth"],
})

# Auto-generate columns from df.columns
DataTable(rows=df)

# Custom columns — you control headers, sorting, formatting
DataTable(
    columns=[
        DataTableColumn(key="name", header="Hitchhiker", sortable=True),
        DataTableColumn(key="score", header="Score", align="right", format="number"),
    ],
    rows=df,
)
```

Works with any object that has `.columns` and `.to_dict(orient="records")` (pandas) or `.to_dicts()` (polars).

## API Reference

<Card icon="code" title="DataTable Parameters">
<ParamField body="columns" type="list[DataTableColumn]" required>
  Column definitions. Auto-generated from DataFrame column names when `rows` is a DataFrame and `columns` is omitted.
</ParamField>

<ParamField body="rows" type="list[dict] | str | DataFrame" default="[]">
  Row data as a list of dicts, a `{{ field }}` interpolation reference, or a pandas/polars DataFrame.
</ParamField>

<ParamField body="search" type="bool" default="False">
  Show a search input above the table for narrowing visible rows.
</ParamField>

<ParamField body="paginated" type="bool" default="False">
  Show pagination controls below the table.
</ParamField>

<ParamField body="page_size" type="int" default="10">
  Number of rows per page when `paginated=True`.
</ParamField>

<ParamField body="on_row_click" type="Action | list[Action] | None" default="None">
  Action(s) to fire when a row is clicked. `$event` is the row's data dict. The clicked row highlights visually.
</ParamField>

<ParamField body="css_class" type="str | None" default="None">
  Additional Tailwind CSS classes for the table wrapper.
</ParamField>
</Card>

<Card icon="code" title="DataTableColumn Parameters">
<ParamField body="key" type="str" required>
  Data key — matches keys in the row dicts.
</ParamField>

<ParamField body="header" type="str" required>
  Column header display text.
</ParamField>

<ParamField body="sortable" type="bool" default="False">
  Enable click-to-sort on this column.
</ParamField>

<ParamField body="format" type="str | None" default="None">
  Expression pipe to apply to every cell value (e.g. `"currency"`, `"percent:1"`, `"date:long"`). Uses the same pipe system as `{{ value | pipe }}` expressions. Ignored when the cell value is a component.
</ParamField>

<ParamField body="width" type="str | None" default="None">
  Column width as a CSS value (e.g. `"200px"`, `"30%"`).
</ParamField>

<ParamField body="min_width" type="str | None" default="None">
  Minimum column width as a CSS value.
</ParamField>

<ParamField body="max_width" type="str | None" default="None">
  Maximum column width as a CSS value.
</ParamField>

<ParamField body="align" type="str | None" default="None">
  Text alignment for both header and cells: `"left"`, `"center"`, or `"right"`. Resolves to `header_class` and `cell_class` Python-side.
</ParamField>

<ParamField body="header_class" type="str | None" default="None">
  Tailwind classes applied to the header cell for this column.
</ParamField>

<ParamField body="cell_class" type="str | None" default="None">
  Tailwind classes applied to every data cell in this column.
</ParamField>
</Card>

## Protocol Reference

```json DataTable
{
  "type": "DataTable",
  "columns": "[DataTableColumn] (required)",
  "rows?": "dict[] | string",
  "search?": false,
  "paginated?": false,
  "pageSize?": 10,
  "onRowClick?": "Action | Action[]",
  "cssClass?": "string"
}
```

```json DataTableColumn
{
  "key": "string (required)",
  "header": "string (required)",
  "sortable?": false,
  "format?": "string",
  "width?": "string",
  "minWidth?": "string",
  "maxWidth?": "string",
  "headerClass?": "string",
  "cellClass?": "string"
}
```

For the complete protocol schema, see [DataTable](/protocol/data-table), [DataTableColumn](/protocol/data-table-column).
