# Tables in your AI client

> The words that format a table in Bonnard, and the options and limits the product enforces.

Updated: 2026-10-12

Bonnard draws a table to show the rows themselves, with a label and a format per column.

[Image: A chat with the question "Revenue and orders by region last quarter, as a table.", three tool calls, and the table Bonnard drew: revenue and orders for five regions in Q3 2026, the UK first]

The examples use Acme, a demo retailer that reports in pounds. Each variant is a few words in the question. A follow-up changes a table already drawn.

## How an agent draws it

The chat above ran three tools, in this order:

1. `explore_schema` finds the tables and their columns: here `orders`, one row per order, with `order_date`, `region` and `revenue`.
2. `sql_query` runs a read-only query, so the agent can check the numbers first.
3. `render_chart` takes the SQL and the chart options. It runs the query and returns the table the chat draws.

The `render_chart` call behind the table above:

```json
{
  "charts": [
    {
      "sql": "SELECT region, SUM(revenue) / 1000 AS revenue, COUNT(*) AS orders FROM orders WHERE order_date >= DATE '2026-07-01' AND order_date < DATE '2026-10-01' GROUP BY region ORDER BY revenue DESC",
      "chartType": "table",
      "title": "Revenue and orders by region, Q3 2026",
      "fields": [
        { "name": "region", "label": "Region" },
        { "name": "revenue", "label": "Revenue (£k)", "format": "number" },
        { "name": "orders", "label": "Orders", "format": "number" }
      ]
    }
  ]
}
```

A call takes up to 10 charts, shown as a carousel. Each example below shows its one entry. A connected agent reads the full option reference with `explore_docs`, topic `charts/table`.

## Currency

```prompt
Revenue and average order value by region last quarter, in pounds.
```

[Image: Revenue and average order by region in pounds: whole pounds for revenue, pounds and pence for the average order]

```json
{
  "sql": "SELECT region, SUM(revenue) AS revenue, SUM(revenue) / COUNT(*) AS average_order FROM orders WHERE order_date >= DATE '2026-07-01' AND order_date < DATE '2026-10-01' GROUP BY region ORDER BY revenue DESC",
  "chartType": "table",
  "title": "Revenue and average order by region, Q3 2026",
  "fields": [
    { "name": "region", "label": "Region" },
    { "name": "revenue", "label": "Revenue", "format": "currency", "currency": "GBP" },
    { "name": "average_order", "label": "Average order", "format": "currency", "currency": "GBP" }
  ]
}
```

`currency` takes an ISO 4217 code, here GBP, and shows its symbol. A whole amount has no decimals, and an amount with pence has two.

## Percentages

```prompt
September revenue by category, with each category's share of the total.
```

[Image: September revenue by category with each category's share as a percentage, outerwear first at 21.8%]

```json
{
  "sql": "SELECT category, SUM(revenue) / 1000 AS revenue, SUM(revenue) / SUM(SUM(revenue)) OVER () AS share FROM order_lines WHERE order_date >= DATE '2026-09-01' AND order_date < DATE '2026-10-01' GROUP BY category ORDER BY revenue DESC",
  "chartType": "table",
  "title": "Revenue by category, September 2026",
  "fields": [
    { "name": "category", "label": "Category" },
    { "name": "revenue", "label": "Revenue (£k)", "format": "number" },
    { "name": "share", "label": "Share", "format": "percent" }
  ]
}
```

The query returns each share as a fraction, and `percent` shows 0.2176 as 21.8%. A column of 0 to 100 values reads the same.

## Short numbers

```prompt
Revenue by channel for each quarter this year, shortened to K and M.
```

[Image: Revenue by channel for Q1 to Q3 2026 in short numbers, from 3.2M for online to 591.5K for marketplace]

```json
{
  "sql": "SELECT channel, SUM(revenue) FILTER (WHERE order_date < DATE '2026-04-01') AS q1, SUM(revenue) FILTER (WHERE order_date >= DATE '2026-04-01' AND order_date < DATE '2026-07-01') AS q2, SUM(revenue) FILTER (WHERE order_date >= DATE '2026-07-01') AS q3 FROM orders WHERE order_date >= DATE '2026-01-01' AND order_date < DATE '2026-10-01' GROUP BY channel ORDER BY q3 DESC",
  "chartType": "table",
  "title": "Revenue by channel and quarter, 2026 (£)",
  "fields": [
    { "name": "channel", "label": "Channel" },
    { "name": "q1", "label": "Q1", "format": "compact" },
    { "name": "q2", "label": "Q2", "format": "compact" },
    { "name": "q3", "label": "Q3", "format": "compact" }
  ]
}
```

`compact` writes 3,209,800 as 3.2M and 893,200 as 893.2K. Without it, a table shows every digit.

## Every row

```prompt
Revenue, orders and conversion for every week of last quarter.
```

[Image: Thirteen weeks of revenue, orders and conversion, with the first ten weeks in view]

```json
{
  "sql": "SELECT week_start AS week, revenue / 1000 AS revenue, orders, conversion FROM weekly_trading WHERE week_start >= DATE '2026-06-29' AND week_start < DATE '2026-09-28' ORDER BY week_start",
  "chartType": "table",
  "title": "Weekly trading, Q3 2026",
  "fields": [
    { "name": "week", "label": "Week" },
    { "name": "revenue", "label": "Revenue (£k)", "format": "number" },
    { "name": "orders", "label": "Orders", "format": "number" },
    { "name": "conversion", "label": "Conversion", "format": "percent" }
  ]
}
```

The table holds every row the query returns. A long one scrolls inside the chat and pages on a phone. A tap on a header sorts by that column, descending first, and a third tap restores the query's order.

## Options and limits

Table: What a table takes

| Ask for | What Bonnard draws |
| --- | --- |
| "as a table" | Every column and row, in the query's order |
| "in pounds" or "in euros" | Amounts with the currency's symbol |
| "as a percentage" | Values with one decimal, from 0 to 1 fractions or 0 to 100 |
| "shortened to K and M" | Numbers such as 1.2M and 893.2K |
| "call the column Net revenue" | A label in place of the column's SQL name |
| "the top 10" | The 10 rows the query returns |
| "sorted by orders" | Rows in that order, which the reader can sort again |

- A table takes `fields` alone: a label and a format per column. `encode`, axes, stacking, reference lines and styling are refused, with the options a table takes.
- Formats are never inferred from column names. Money and ratios show as plain numbers until a format is set.
- Without a label, a snake_case name shows with spaces and a capital: `average_order` reads "Average order".
- Every row the query returns is shown. A chart query returns up to 5,000 rows by default.
- When the rows run past what one reply can carry, the table keeps the first rows and tells the agent how many.
- Dates show as 29 Jun 2026, and date-times as 29 Jun 26, 14:05.
- There is no pivot table. A column per period comes from the SQL, as the quarters above do.
- On a dashboard, a table is a chart tile with `chartType: "table"`.
