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.
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.
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/chartsThe 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.mdIt 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.
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:
| State | How it renders |
|---|---|
| Ready | Parses the tool input or output and passes it through toProps. |
| Loading | Uses loadingProps while the AI SDK part is still input-streaming or the host sets toolMetadata.fableState to loading. |
| Empty | Uses emptyProps when the host sets toolMetadata.fableState to empty. |
| Error | Uses errorProps when the AI SDK part errors or when the payload fails schema validation. |
| Disabled | Renders 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.
---
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.