Xzero UI
Pro library

Ask your dataPro

A natural language to chart card: you ask a question, PostgreSQL streams into a collapsible, syntax highlighted panel, the query runs with a rolling row count, and the answer builds as stacked bars, lines or a table, with a one line insight streamed in underneath.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui-pro/ask-your-data
Live · keyboard ready

When to use

When not to use

Installation

Register both Xzero UI namespaces in your project's components.json:

{
  "registries": {
    "@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
    "@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
  }
}
npx shadcn@4.23.0 add @xzero-ui-pro/ask-your-data

Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.

Usage

import { AskYourData } from "@/components/xzero/ask-your-data/ask-your-data";

// Self playing preview with sample data
<AskYourData />

// Wired to your model and a read only database role
export function Explore() {
  return (
    <AskYourData
      source={{ name: "analytics", dialect: "PostgreSQL" }}
      generateSql={(question, { signal }) => streamSql(question, { signal })} // AsyncIterable<string>
      runQuery={async (sql, { signal, onProgress }) => {
        const res = await fetch("/api/query", { method: "POST", body: JSON.stringify({ sql }), signal });
        if (!res.ok) throw new Error(await res.text()); // shows the database error
        const { rows, ms } = await res.json();
        onProgress(rows.length, rows.length);
        return {
          title: "Weekly signups by plan",
          categories: weeksOf(rows),
          series: seriesByPlan(rows),
          rowCount: rows.length,
          durationMs: ms,
        };
      }}
      onResult={(result, { sql }) => saveToHistory(result.title, sql)}
    />
  );
}

API reference

AskYourData

Natural language to chart in one card. A question field with the data source, a collapsible Generated SQL panel where PostgreSQL streams in with syntax tints, a running state with a rolling row count, then a result that builds as stacked bars, lines or a table, with a legend, a hover and keyboard readout, and a streamed one line insight. With no props it asks "Weekly signups by plan since August" against sample data once it scrolls into view.

PropTypeDefaultDescription
questionstring–Controlled question text. Pair with onQuestionChange.
defaultQuestionstring"Weekly signups by plan since August"Starting question when uncontrolled; autoRun asks it.
onQuestionChange(question: string) => void–Called as the person types or picks a suggestion.
placeholderstring"Ask a question about your data"Placeholder for the empty field.
source{ name: string; dialect?: string; mark?: ReactNode }{ name: "analytics", dialect: "PostgreSQL" }The database the question runs against. The name shows in the field and the empty state; the dialect leads the SQL meta.
suggestionsstring[]three sample questionsQuestions offered in the empty state and after a question that matches no data. Pass [] to hide them.
generateSql(question, { signal, source }) => Promise<string | AsyncIterable<string>> | string | AsyncIterable<string>–Writes SQL for the question. Return the whole string and the card streams it in four words a tick at a fixed final size, or return an async iterable of chunks from your model and the panel grows with them. Respect signal: Stop aborts it. Without it a sample model answers.
runQuery(sql, { signal, onProgress, question }) => Promise<AskYourDataResult>–Runs the SQL (read only) and returns a chart ready result. Call onProgress(rowsScanned, totalRows?) to roll the count in the running state; passing the total reserves its width. Throw to show the database error. Without it sample data answers.
onResult(result: AskYourDataResult, details: { question: string; sql: string }) => void–Called once a result is charted, for saving the question to a dashboard or logging.
onError(error: unknown, details: { question: string; stage: "sql" | "query" }) => void–Called when writing or running fails. Not called when the person stops a run.
chart"bar" | "line" | "table"–Controlled view. Pair with onChartChange.
defaultChart"bar" | "line" | "table""bar"View for results that do not set result.chart.
onChartChange(chart: "bar" | "line" | "table") => void–Called when the person switches the view.
autoRunbooleantrue without generateSqlAsks the starting question once the card is 35% on screen. Off by default when you wire a real model.
keepSqlOpenbooleanfalseKeeps the SQL panel open after the query starts instead of folding it away. Once the person opens or closes the panel, their choice wins.
classNamestring–Class on the card.

AskYourDataResult

What runQuery returns, shaped for the chart.

PropTypeDefaultDescription
title (required)string–Chart title, such as "Weekly signups by plan".
subtitlestring–Quiet line under the title, such as "Aug 3 to Oct 25 · 1,284 signups".
categories (required)string[]–X axis labels in order, such as week starts. Labels thin out on narrow cards.
series (required){ id: string; label: string; values: number[]; color?: string }[]–One entry per plan, region or metric, with one value per category. Colors default to --series-1 through --series-4.
stackedbooleantrueSeries add up: bars stack, and the legend, tooltip and table show totals. Set false for rates and averages; bars then group side by side.
legendValue"sum" | "last" | "none""sum" when stacked, else "none"What follows each legend label: the series total for flows like signups, the latest value for levels like MRR, or nothing.
chart"bar" | "line" | "table"–View to open on for this result.
categoryLabelstring"Category"Header of the table's first column, such as "Week".
formatValue(value: number) => stringen-US groupingFormats axis ticks, legend totals, tooltip and table cells, such as $258k or 3.9%.
insightstring–One or two sentences that read the chart. Streams in under it with its final height reserved.
rowCountnumber–Rows the query returned, shown in the SQL meta as "48 rows".
durationMsnumber–Query time, shown as 214ms or 1.2s.

Keyboard interactions

KeysAction
EnterIn the question field, asks the question. While writing or running, the same button stops the run.
EscapeIn the question field, stops a run in progress.
TabMoves through the field, Ask, the Generated SQL row, Copy SQL, the code block (scrollable), the chart switch, the chart, and suggestions or Try again.
Enter, SpaceOn the Generated SQL row, opens or closes the SQL.
Left, Right, Home, EndOn the chart, moves the readout between categories; on the chart switch, changes the view.
Escape (chart)Clears the chart readout.

Accessibility

Motion

Responsive behavior

Performance

Notes for AI

Also in ai

Guidance for AI tools

Ask your data: A natural language to chart card: you ask a question, PostgreSQL streams into a collapsible, syntax highlighted panel, the query runs with a rolling row count, and the answer builds as stacked bars, lines or a table, with a one line insight streamed in underneath. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://ui.xzero.co.uk/llms.txt

Browse all components