2

Charts Beta

Render static chart payloads from validated AI tool calls.

Charts renders static, display-ready chart data as line, bar, or pie charts using shadcn chart conventions and Recharts. It is meant for AI answers where the data is already known, validated, and safe to display.

The model should use show_chart when the response would be clearer as a visual comparison, trend, or part-to-whole breakdown. The host app still owns live data access. If the chart is based on private orders, billing, analytics, or database rows, fetch and authorize that data in your backend first, then pass a small display-ready payload into the tool.

Monthly signups
Static AI-provided chart data.
import { Charts } from "@/components/fable-ui/charts/charts"

const data = [
  { month: "Jan", direct: 120, referral: 84 },
  { month: "Feb", direct: 148, referral: 96 },
  { month: "Mar", direct: 172, referral: 118 },
  { month: "Apr", direct: 196, referral: 132 },
  { month: "May", direct: 214, referral: 151 },
]

Pie chart preview

Pie charts use categoryKey and valueKey instead of xKey and multi-series values. Use them for part-to-whole slices where every row is one category.

Signup channel mix
Part-to-whole breakdown for the current period.
import { Charts } from "@/components/fable-ui/charts/charts"

const data = [
  { channel: "Direct", value: 38 },
  { channel: "Referral", value: 27 },
  { channel: "Organic", value: 21 },
  { channel: "Paid", value: 14 },
]

export function ChannelMixPieChart() {

Installation

pnpm dlx shadcn@latest add shobky/fable-ui/charts

The charts registry item installs the component, the tool definition, the tool re-export, and its routing docs:

components/fable-ui/charts/index.ts
components/fable-ui/charts/charts.tsx
components/fable-ui/charts/charts.types.ts
components/fable-ui/charts/tool-definition.ts
lib/fable-ui/tools/show-chart-tool.ts
lib/fable-ui/manifests/show-chart.md

It also installs or expects the shared core registry dependency, plus shadcn UI pieces used by the chart surface.

Usage

Use line or bar charts with xKey and series:

import { Charts } from "@/components/fable-ui/charts/charts"
 
const data = [
  { month: "Jan", direct: 120, referral: 84 },
  { month: "Feb", direct: 148, referral: 96 },
  { month: "Mar", direct: 172, referral: 118 },
]
 
export function RevenueCharts() {
  return (
    <Charts
      title="Monthly signups"
      description="Static AI-provided chart data."
      data={data}
      xKey="month"
      series={[
        { key: "direct", label: "Direct" },
        { key: "referral", label: "Referral" },
      ]}
      availableChartTypes={["line", "bar"]}
      defaultChartType="line"
    />
  )
}

Use pie charts with categoryKey and valueKey:

import { Charts } from "@/components/fable-ui/charts/charts"
 
const data = [
  { channel: "Direct", value: 38 },
  { channel: "Referral", value: 27 },
  { channel: "Organic", value: 21 },
  { channel: "Paid", value: 14 },
]
 
export function ChannelMixPieChart() {
  return (
    <Charts
      title="Signup channel mix"
      description="Part-to-whole breakdown for the current period."
      data={data}
      categoryKey="channel"
      valueKey="value"
      availableChartTypes={["pie", "bar"]}
      defaultChartType="pie"
      format={{ value: "percent" }}
    />
  )
}

Tool definition

The AI SDK tool name is show_chart.

components/fable-ui/charts/tool-definition.ts defines the Zod schema, the Vercel AI SDK tool, and the renderer contract that turns a tool part into props for <Charts />. The chart component is lazy-loaded from this definition so AI routes can import the definition and pass only showChart.tool without eagerly loading Recharts.

components/fable-ui/charts/tool-definition.ts
import { lazy } from "react"
import { tool } from "ai"
import { z } from "zod"

import { defineFableComponent } from "@/lib/fable-ui/core"
import { chartTypes } from "./charts.types"

const Charts = lazy(() =>
  import("./charts").then((module) => ({ default: module.Charts })),
)

const chartTypeSchema = z.enum(chartTypes)
const chartValueSchema = z.union([
  z.string(),
  z.number(),
  z.boolean(),
  z.null(),
])
const chartDataRowSchema = z.record(z.string(), chartValueSchema)

const chartSeriesSchema = z.object({
  key: z.string().min(1),
  label: z.string().min(1).optional(),
  color: z.string().min(1).optional(),
})

const chartFormatSchema = z.object({
  value: z.enum(["number", "currency", "percent"]).optional(),
  locale: z.string().min(1).optional(),
  currency: z.string().min(1).optional(),
  compact: z.boolean().optional(),
})

export const showChartInputSchema = z.object({
  title: z.string().min(1),
  description: z.string().optional(),
  context: z.string().optional(),
  data: z.array(chartDataRowSchema).max(200),
  xKey: z.string().min(1).optional(),
  categoryKey: z.string().min(1).optional(),
  valueKey: z.string().min(1).optional(),
  series: z.array(chartSeriesSchema).min(1).max(8).optional(),
  availableChartTypes: z.array(chartTypeSchema).min(1).max(3).optional(),
  defaultChartType: chartTypeSchema.optional(),
  format: chartFormatSchema.optional(),
  emptyState: z
    .object({
      title: z.string().min(1),
      description: z.string().optional(),
    })
    .optional(),
})

export type ShowChartInput = z.infer<typeof showChartInputSchema>

export function createShowChartTool() {
  return tool({
    description:
      "Show a chart from static, display-ready data already available in the conversation. Supports line, bar, and pie chart payloads. Do not fetch model-owned URLs, invent data, run SQL, or perform host data access.",
    inputSchema: showChartInputSchema,
    execute: async (input) => input,
  })
}

export const showChartTool = createShowChartTool()

export const showChart = defineFableComponent({
  name: "show_chart",
  schema: showChartInputSchema,
  tool: showChartTool,
  renderer: {
    Component: Charts,
    loadingProps: { title: "Charts", data: [], isLoading: true },
    emptyProps: { title: "Charts", data: [] },
    errorProps: (description: string) => ({
      title: "Charts unavailable",
      data: [],
      error: { title: "Charts unavailable", description },
    }),
    toProps: (data: ShowChartInput) => ({ ...data }),
  },
})

Payload

{
  title: string
  description?: string
  context?: string
  data: Record<string, string | number | boolean | null>[]
  xKey?: string
  categoryKey?: string
  valueKey?: string
  series?: { key: string; label?: string; color?: string }[]
  availableChartTypes?: ("line" | "bar" | "pie")[]
  defaultChartType?: "line" | "bar" | "pie"
  format?: {
    value?: "number" | "currency" | "percent"
    locale?: string
    currency?: string
    compact?: boolean
  }
}

The schema validates that the model returned a chart title, an array of rows, supported chart types, and valid formatting options. series is used for line and bar charts. categoryKey and valueKey are used for pie charts.

States

The renderer maps tool part states to component props:

StateHow it renders
ReadyParses the tool input or output and passes it through toProps.
LoadingUses loadingProps while the AI SDK part is still input-streaming or the host sets toolMetadata.fableState to loading.
EmptyUses emptyProps when the host sets toolMetadata.fableState to empty.
ErrorUses errorProps when the AI SDK part errors or when the payload fails schema validation.
DisabledRenders parsed props with isDisabled when the host sets toolMetadata.fableState to disabled.

Manifest

The manifest is the model-routing contract for show_chart. It explains when to select charts, when to avoid them, and which neighboring component should win instead. Read the Manifests guide for the full convention.

lib/fable-ui/manifests/show-chart.md
---
tool: show_chart
component: Charts
---

# show_chart

Use `show_chart` when the assistant has static, display-ready data that is best understood as a line, bar, or pie chart.

Rules:

- Keep model-provided chart data small and explicit.
- Use `line` for trends over an ordered x-axis, `bar` for category comparison, and `pie` for part-to-whole slices.
- Include `xKey` and `series` for line/bar charts.
- Include `categoryKey` and `valueKey` for pie charts.
- Set `availableChartTypes` when the same payload can be viewed in more than one chart type.
- Do not let the model fetch URLs, query databases, run SQL, or decide authorization.
- For host-owned data, ask the host application to provide validated rows before calling this tool.

Rendering uses shadcn chart conventions on top of Recharts. Static model-provided rows are supported; developer-owned data fetching should happen outside the tool payload and pass validated rows into the component.

When to use

Use show_chart when the assistant already has static, display-ready data that is best read visually.

Use line charts for trends, bar charts for comparisons, and pie charts for part-to-whole slices. Set availableChartTypes when the same payload can be explored in more than one view.

The model must not fetch URLs, query databases, run SQL, or decide authorization. For live or private data, the host app should fetch and validate rows first, then pass safe static data into the chart payload.