1 prompt, 88 seconds: I added a new chart type to AG Studio dashboard

  |   AG Studio

AG Charts just shipped a quadrant chart and it looks great.

We are hosting a workshop on data storytelling with David McCandless (Information is Beautiful) where the participants will visualize data using AG Studio, our dashboarding framework. I thought a quadrant chart would be a fantastic addition to the data viz lexicon for the attendees to choose from. I asked Opus 5.5 to bring it in, and 88 seconds later it was sitting in the widget menu.

Honestly, that speed is less about the model and more about how AG Studio is built: adding a chart type is a small, typed, declarative job, which is exactly what AI agents are good at.

The quadrant, 88 seconds in. Sixteen products by revenue and growth, sized by customers, split at the medians into four named regions.

If you haven't met one yet, a quadrant chart is a scatter plot split into four named regions around a pivot. It's a brilliant way to turn a cloud of points into a story: the stars, the sleepers, the ones to watch.

AG Charts had already done the hard part, the drawing. What's left is wiring: take the fields someone picks, query the data, hand the rows to the chart. AG Studio's custom widget API keeps that wiring small. Small enough, it turns out, for one prompt.

The ask

AG Charts just released a quadrant chart. Add it to this app as a custom widget: a label, an x measure, a y measure and a bubble size. Register it in the widget menu and put an example on the gallery page.

The clock

ElapsedWhat had happened
46 sWidget, its type, the registry entry and a gallery card written. Type-check clean.
65 sProduction build green.
88 sRunning in the app: in the widget menu, drawing data, no console errors.

One prompt, one pass, under two minutes. So why is it that quick? Mostly because of how little AG Studio asks of a widget.

It's just a React component

The workshop starter is plain TypeScript, and that's the version I timed. Most people will be writing React, though, and there the same widget is an ordinary function component:

export function QuadrantWidget(params: AgWidgetParams<QuadrantDef>) {
    const { api, widgetApi, dataMapping } = params;
    const label = dataMapping.label?.[0];
    const x = dataMapping.x?.[0];
    const y = dataMapping.y?.[0];
    const size = dataMapping.size?.[0];
    const [points, setPoints] = useState<Point[] | null>(null);

    useEffect(() => {
        if (!label || !x || !y || !size) {
            widgetApi.setDisplayState('incompleteDataMapping');
            return;
        }
        widgetApi.setDisplayState('loading');
        let cancelled = false;
        widgetApi.getData({ fields: [label, x, y, size] }).then(({ results }) => {
            if (cancelled) return;
            setPoints(results.rows.map((row) => ({
                label: row[label.key],
                x: Number(row[x.key]),
                y: Number(row[y.key]),
                size: Number(row[size.key]),
            })));
            widgetApi.setDisplayState('displayed');
        });
        return () => { cancelled = true; };
    }, [widgetApi, label, x, y, size]);

    if (!points) return null;
    const theme = getChartTheme(api);
    return (
        <AgQuadrantChart options={{
            theme: {
                ...theme,
                params: { ...theme.params, backgroundColor: 'transparent' },
            },
            data: points,
            labelKey: 'label', xKey: 'x', yKey: 'y', sizeKey: 'size',
            pivot: {
                x: median(points.map((p) => p.x)),
                y: median(points.map((p) => p.y)),
            },
        }} />
    );
}

No base class, no lifecycle methods to learn, no framework of ours to adopt. AG Studio hands the component its field mapping and a widgetApi, and the component renders whatever it likes. Here that's AgQuadrantChart from ag-charts-react. The quadrant is a preset rather than an ordinary series, with its own constructor, and it didn't matter one bit: AG Studio only cares that it gets a component back.

A dozen lines of config become the whole editing UI

Register it, and that's the only other thing you write:

{
    id: 'quadrant-chart',
    label: 'Quadrant',
    icon: quadrantIcon,
    dataMapping: { label: category(), x: numeric(), y: numeric(), size: numeric() },
    form: formWithoutCrossFilter([
        { key: 'label', label: 'Label' },
        { key: 'x', label: 'X value' },
        { key: 'y', label: 'Y value' },
        { key: 'size', label: 'Size' },
    ]),
    comp: QuadrantWidget,
}
AG Studio's setup panel for the new widget. The widget type reads Custom Widget, Quadrant. Below it are four named data slots: Label holding Product, X value holding Revenue as a sum, Y value holding Growth as an average, and Size holding Customers as a sum.
The setup panel AG Studio built from that entry. I wrote no UI code for it.

From those few lines AG Studio builds everything around the chart: the entry in the widget menu, the setup panel with four named slots, drag and drop from the data panel, and the rule that a numeric() slot won't take a text field. Anyone building a report can remap the chart to different columns, or switch revenue from a sum to an average, without touching the code again.

The data layer is already done

Look at the getData call above. That's the entire data layer. The widget never sees a database, a query language or a grouping step. It asks for the four fields someone mapped, and the rows come back already grouped by product and aggregated the way the panel says: revenue summed, growth averaged. AG Studio's engine does all of that in the browser. The only real decision left for the widget is where to put the pivot, and splitting at the medians keeps every region busy.

Loading, saving and AG Charts theming come with it

  • Loading and empty states. One call to setDisplayState gives the widget the same spinner and "no data" treatment as every built-in widget.
  • Saving and sharing. The widget's field choices are part of the report's state, so saving, exporting and importing a report carries it along for free.
  • Theming, for AG Charts. getChartTheme(api) hands the chart the report's palette and fonts, so it matches the built-in widgets and follows the theme switcher. That works because the quadrant is an AG Charts chart. Draw a widget with a different library and you'll style it yourself for now; bringing the report theme to any custom widget is something we may add down the line.

Cross-filtering is the one piece you opt into rather than inherit. By default a widget's data already respects selections made in other widgets. What it won't do on its own is filter other widgets when someone clicks it: the developer writing the widget adds that with one call to toggleCrossFilter in its click handler. And whoever is building the report can choose how each widget responds to selections, filtering, highlighting or ignoring them, right in the setup panel.

A well-designed, typed API is what makes it agent-friendly

To be clear, we didn't design AG Studio's architecture with coding agents in mind. It's just a well-designed, typed interface: a component, a registry entry, one call for data. That turns out to be exactly what makes it easy for an agent to implement. There's very little to decide, very little to get wrong, and the type checker points out the rest before the page ever loads.

And that's the real headline. When AG Charts ships something new on a Tuesday, an AG Studio dashboard can have it on Tuesday too. The gap between the two releases is one prompt.

⭐
Why AG Studio suits this
• A widget is just a component. A React function component (or Angular, Vue or plain TypeScript) plus a registry entry.
• Config becomes UI. Declare the fields a chart needs, and AG Studio builds the menu entry, the setup panel and the validation.
• The data layer is done. A widget asks for fields and gets rows back, already grouped and aggregated.
• The plumbing comes with it. Loading states and saving are built in, and AG Charts widgets pick up the report's theme.
• It's typed end to end. The compiler checks an agent's work before the page ever loads.

Try it!

The workshop starter is public at ag-grid.github.io/ag-studio-iib-workshop, preloaded with data from Wikipedia's lamest edit wars, a quadrant among its custom widgets, and every built-in one besides.

Read more posts about...