Ask your data

Pro

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.

Live · keyboard ready
Generated SQLPostgreSQL · analyticsanalytics
Ask in plain wordsAnswers are charted from a read only query on analytics
  • Letting people ask product or business questions in plain words and see a chart, with the SQL one click away to verify or copy.
  • Embedding an analytics answer in a dashboard card, an admin page or an AI assistant's result view.
  • Teaching or auditing text to SQL, where showing the generated query builds trust.
  • Use bar-chart or line-chart when the query is fixed and there is no question to ask.
  • Use tool-call to show a database query as one step inside a longer agent transcript.
  • Use sql-editor when people write and tune queries by hand.

Installation

Pro source and install commands unlock with a Pro plan.

example.tsx
import { AskYourData } from "@/components/arc/ask-your-data/ask-your-data"; // Self playing preview with sample data<AskYourData /> // Wired to your model and a read only database roleexport 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

2 parts. The first is the root.

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
titleRequiredstring–Chart title, such as "Weekly signups by plan".
subtitlestring–Quiet line under the title, such as "Aug 3 to Oct 25 · 1,284 signups".
categoriesRequiredstring[]–X axis labels in order, such as week starts. Labels thin out on narrow cards.
seriesRequired{ 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.
Enter
In the question field, asks the question. While writing or running, the same button stops the run.
Escape
In the question field, stops a run in progress.
Tab
Moves 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, Space
On the Generated SQL row, opens or closes the SQL.
Left, Right, Home, End
On the chart, moves the readout between categories; on the chart switch, changes the view.
Escape (chart)
Clears the chart readout.
  • A polite live region narrates the run: writing SQL, running the query, then the chart title, row count and insight. Without generateSql and runQuery it adds "Simulated with sample data, nothing was queried."
  • Status is never color alone: a spinner, a drawn check, an alert or a stop glyph sits beside a text label in the SQL row and the stage header.
  • The SQL row is a real button with aria-expanded and aria-controls, labelled with its title and meta, such as "Generated SQL, PostgreSQL · 48 rows · 214ms". Text not yet streamed is hidden from assistive technology.
  • The chart is a focusable group: arrow keys move a readout that is announced, such as "Aug 10: Free 61, Pro 26, Team 10, Enterprise 2, total 99". The table view keeps real table semantics with column and row headers.
  • The stage sets aria-busy while working. Text fields get no focus ring by design; the field border shifts to the accent on focus, and every other control uses Arc's shared keyboard ring.
  • SQL streams four tokens every 34ms, each chunk only fading in over 140ms. The full query is laid out invisibly first, so the panel holds its final size and nothing below moves.
  • The SQL panel opens and folds on a 300ms bounce free spring; it folds once the query starts, before the chart builds, so only one region moves at a time.
  • Bars rise from the baseline in 320ms with at most 180ms of stagger; lines draw in 460ms. Switching views crossfades (100ms out, 160ms in) in a fixed area.
  • Header text and the SQL meta crossfade as single lines with a 2px settle. The row count rolls in tabular numerals in reserved width.
  • The insight streams four words a tick with its two lines reserved. Reduced motion shows SQL and insight whole, drops the bar rise, line draw, spinner and breathing skeleton, and keeps every state and announcement. Loops pause off screen.
  • Container aware: below 520px the source keeps only its icon, the chart switch moves into the result above the legend at full width, and the stage grows to fit wrapped legend and insight lines.
  • X axis labels thin out to keep 46px apart, wide SQL lines and tables scroll inside their own wells, and the tooltip flips to whichever side of the column has room.
  • Touch: tap or drag across the chart to read values; vertical scrolling still passes through.
  • Charts are plain SVG measured with one ResizeObserver, no chart library. Text and hairlines are drawn at device pixels, so they stay crisp at any width.
  • The SQL tokenizer is a single regular expression pass, memoized per query. The SQL panel and every view mount only while shown.
  • Rows scanned updates come through onProgress; batch them to a few per second for very fast queries.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Use it for self serve analytics inside a product: a question box on a dashboard, a metrics page, or an admin console. Wire generateSql to your model with the schema in the prompt and runQuery to a read only role with a statement timeout.
  • Return the SQL as an async iterable of model chunks for true streaming, or a whole string for the paced reveal. Always honour signal so Stop really cancels both steps.
  • Shape rows into categories and series in runQuery. Keep series to four or fewer for the default palette; set stacked false for rates or averages so totals are not shown.
  • Let the model write the insight from the returned rows, one or two sentences with concrete numbers. Do not invent numbers that are not in the result.
  • Throw from runQuery with the database message to show it verbatim in the error state with Try again.

The full library index for assistants is at /llms.txt.