Chart (bar)
Bar chart artifact rendering a schema-driven dataset emitted by the model.
Overview
A chart artifact that turns a small JSON payload into a real bar chart.
The model emits an array of { category, value } rows plus a config
that names the series and binds it to a chart token color. The artifact
validates the payload, renders a BarChart from @gremorie/rx-data,
and exposes an accessible data table for screen readers.
This is the canonical example of "schema in, UI out" — the model designs the dataset, the artifact handles axes, ticks, layout, and accessibility.
Preview
| category | Revenue |
|---|---|
| Q1 | 42000 |
| Q2 | 51000 |
| Q3 | 48000 |
| Q4 | 62000 |
{
type: "chart-bar",
data: Array<{ category: string; value: number }>,
config: { xKey: string; yKey: string; label: string },
}Schema
The LLM returns structured output matching this shape:
{
type: "chart-bar",
data: Array<{ category: string; value: number }>,
config: { xKey: string; yKey: string; label: string },
}Anatomy
Card
├─ CardHeader CardTitle + Badge (outline) — artifact type
├─ BarChart (rx-data) renders the dataset; ChartConfig maps value key → label + color token
└─ schema preview card exposes the contract for inspectionInstallation
npx gremorie@latest add artifact-chart-barpnpm dlx gremorie@latest add artifact-chart-baryarn dlx gremorie@latest add artifact-chart-barbunx --bun gremorie@latest add artifact-chart-barPrompt examples
Sample prompts that produce valid output for this artifact:
- "Show me Q3 revenue by region as a bar chart."
- "Compare monthly active users for the last 6 months."
- "Plot ticket counts by priority bucket."
Code
'use client';
import {
Badge,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@gremorie/rx-display';
import { BarChart, type ChartConfig, type ChartDatum } from '@gremorie/rx-data';
const SCHEMA = `{
type: "chart-bar",
data: Array<{ category: string; value: number }>,
config: { xKey: string; yKey: string; label: string },
}`;
const DATA: ChartDatum[] = [
{ category: 'Q1', value: 42000 },
{ category: 'Q2', value: 51000 },
{ category: 'Q3', value: 48000 },
{ category: 'Q4', value: 62000 },
];
const CONFIG: ChartConfig = {
value: { label: 'Revenue', color: 'var(--chart-1)' },
};
export function ChartBar() {
return (
<div className="flex w-full flex-col gap-4">
<Card>
<CardHeader>
<div className="flex items-center justify-between gap-3">
<div>
<CardTitle>Quarterly revenue</CardTitle>
<CardDescription>
Generated from a chart schema returned by the LLM.
</CardDescription>
</div>
<Badge variant="outline">JSON Schema</Badge>
</div>
</CardHeader>
<CardContent>
<BarChart data={DATA} config={CONFIG} xKey="category" />
</CardContent>
</Card>
<Card className="bg-muted/30">
<CardHeader>
<CardTitle className="text-sm">Schema</CardTitle>
</CardHeader>
<CardContent>
<pre className="overflow-x-auto text-xs font-mono text-muted-foreground">
{SCHEMA}
</pre>
</CardContent>
</Card>
</div>
);
}The Angular edition ships the ChartArtifact shell from @gremorie/ng-artifacts,
which wraps any @gremorie/ng-data chart with a chart ⇄ table toggle and
PNG / CSV downloads, plus the same accessible data table:
import { Component } from '@angular/core';
import { ChartArtifact } from '@gremorie/ng-artifacts';
@Component({
selector: 'app-chart-bar',
imports: [ChartArtifact],
template: `
<chart-artifact
title="Quarterly revenue"
description="Generated from a chart schema returned by the LLM."
type="bar"
categoryKey="category"
valueKey="value"
valueLabel="Revenue"
[data]="data"
/>
`,
})
export class ChartBarComponent {
readonly data = [
{ category: 'Q1', value: 42000 },
{ category: 'Q2', value: 51000 },
{ category: 'Q3', value: 48000 },
{ category: 'Q4', value: 62000 },
];
}Pass type (bar | area | line | pie | radar | radial | scatter) to switch the
embedded chart, or a valueKey array for multi-series. The shell mirrors the
React ChartArtifact input for input.
ChartArtifact props
Generated from the agnostic component contract (@gremorie/contracts), so the
React and Angular editions and this table never drift:
| Prop | Type | Default | Description |
|---|---|---|---|
title* | string | - | Single-line heading. |
description | string | - | Optional supporting line (truncates). |
data* | ChartArtifactDatum[] | - | Tabular rows: one object per category / point. |
type | 'bar' | 'area' | 'line' | 'pie' | 'radar' | 'radial' | 'scatter' | 'bar' | Which chart primitive to embed. |
categoryKey* | string | - | Category / X field. |
valueKey* | string | ChartArtifactSeries[] | - | Value field(s): a string for one series, or a series array for multi-series. |
categoryLabel | string | - | Header label for the category column. |
valueLabel | string | - | Label for a single value series. |
defaultView | 'chart' | 'table' | 'chart' | Which view is shown first. |
numberFormat | Intl.NumberFormatOptions | - | Number formatting for the table, CSV and tooltip. |
fileName | string | 'chart' | Base name for downloads. |
icon | LucideIcon | - | Featured icon in the header. |
accent | 'primary' | 'gray' | 'success' | 'error' | 'primary' | Featured-icon color. |
className | string | - | Merged onto the card surface. |
onRegenerate | () => void | - | Wired to the Regenerate item. |
onSave | () => void | - | Wired to the Save item. |
Streaming behavior
This artifact renders after the JSON payload parses. Partial payloads are
deferred until data is a valid array — the chart never renders half a
dataset.