Skip to main content

TABLE / CHART Advanced

Beyond their basic rendering, the table and chart templates each carry a set of advanced capabilities: TABLE can show the SQL that produced it and let the user take the data away, CHART can offer several renderings of one dataset and let the user switch. Neither needs any props — fill the fields in the template and the UI appears.

Pick an Example

Two tabs sit above the table — Table and SQL — and the Table tab's title row carries a download button (CSV / JSON Lines).

This demo uses botProviderEndpoint: "skip" with initMessages, so it needs no connection. The download is generated in the browser from the current data, so it genuinely downloads.
Loading chatbot...

TABLE: the SQL Tab

The table object has two optional fields:

FieldTypePurpose
sqlstringThe query behind the table, shown in the SQL tab as a syntax-highlighted code block
sqlExplanationstringAn explanation, rendered as Markdown below the SQL

Either one being present grows a Table / SQL tab pair above the table; with neither, no tab row renders at all and it is just an ordinary table. The two fields are independent — supplying only sqlExplanation is valid, and the SQL tab then holds just the prose.

TABLE: Download

The download button sits at the right of the title row, on the condition that you are on the Table tab and there is data (it disappears on the SQL tab). It opens two choices:

FormatOutput
CSVComma-separated; a value containing ,, " or a newline is quoted and its " escaped to "". The file is prefixed with a BOM so Excel reads non-ASCII text correctly
JSON LinesOne JSON object per line, keyed by each column's key

The filename is <title>_<YYYY-MM-DD>.<ext>, with non-alphanumeric characters in the title replaced by underscores.

It downloads all the data, not the current page

pagination only governs how many rows are on screen; the download always exports the whole of data. The entire file is generated in the browser from the current data — the backend is never asked again.

Both formats export the formatted display values, not the raw ones — a CURRENCY column comes out as "$135,000", not 135000. If you need the underlying numbers for further computation, this is not the right source.

A snackbar appears afterwards offering to open the file you just downloaded, and dismisses itself after 5 seconds.

TABLE: Columns and Formats

interface TableData {
rowType: "OBJECT" | "ARRAY";
columns: { header: string; key?: string; format?: "DATE" | "DATE_TIME" | "CURRENCY" }[];
pagination: { size: number } | null;
data: Record<string, unknown>[] | unknown[][];
sql?: string;
sqlExplanation?: string;
}

rowType decides how a cell is read out of a row, and what the header looks like:

  • OBJECT — each row is an object, read via column.key. The header shows two lines: header above, key below.
  • ARRAY — each row is an array, read positionally (key is unused). The header is the single header line.

format governs the cell's presentation: DATE goes through toLocaleDateString(), DATE_TIME through toLocaleString(), and CURRENCY formats as TWD with no decimals. Without a format the value is stringified as-is; null / undefined always render as an empty string.

CHART: Switching Between Renderings

chartOptions is an array, one entry per rendering:

FieldTypePurpose
chartOptions[].typestringThe identifier defaultChart matches against
chartOptions[].titlestringThe name shown in the dropdown
chartOptions[].specobjectA Vega or Vega-Lite spec
defaultChartstringThe type selected initially
The dropdown only appears with more than one option

With a single option the chart still renders, but there is nothing to switch to, so no dropdown is drawn.

A defaultChart that matches no type silently falls back to the first option's spec rather than erroring — so a typo just quietly renders the first chart.

{
type: MessageTemplateType.CHART,
title: "Sales by Region",
text: "The same data in two renderings.",
defaultChart: "bar",
chartOptions: [
{ type: "bar", title: "Bar", spec: barSpec },
{ type: "line", title: "Line", spec: lineSpec },
],
}

CHART: the SDK Rewrites Your Spec

The spec you supply is not drawn verbatim. The SDK clones it and changes two things:

  1. The background is forced to white. Deliberately — Vega's default axis and legend text is dark, and a transparent background would make it nearly invisible under a dark theme.
  2. The width tracks the container. A ResizeObserver measures the wrapper and writes it into spec.width, adding autosize: { type: "fit", resize: true, contains: "padding" }. So a width of your own is overwritten; height is left alone.

Vega's built-in actions menu (the save / view-source affordance) is always disabled.

See Also

  • Message Templates — the full template catalog
  • Theme — chat surface colors (the chart background is exempt, see above)