Skip to content

UI Widgets

Think of a specialized LEGO set: each brick has a precise shape (a chart, a table, a map) and snaps together naturally with others. The AI agent is the builder: it picks the bricks and assembles them to create a complete dashboard. UI widgets are those bricks.

A widget is a declarative visual component that the AI agent can instantiate with a single tool call. The agent writes no HTML, no CSS, no JavaScript: it sends a type and a data object, and the widget renders itself.

// The agent calls:
widget_display({ name: "stat", params: { label: "Revenue", value: "$142K", trend: "+12.4%", trendDir: "up" } })
// The canvas instantly displays a KPI card with a green up arrow

Without widgets, an AI agent would need to generate raw HTML to display data. This would create three problems:

  1. Security: LLM-generated HTML could contain malicious code
  2. Consistency: every dashboard would look different
  3. Tokens: describing HTML consumes enormous amounts of tokens

Widgets solve all three: the agent sends structured data (JSON validated against a schema), and the framework handles rendering.

The selection flow follows the recipe and discovery system:

sequenceDiagram
participant LLM as Agent LLM
participant SR as search_recipes
participant GR as get_recipe
participant WD as widget_display
LLM->>SR: search_recipes("kpi")
SR-->>LLM: [{name: "stat", desc: "Key statistic..."}, {name: "stat-card", desc: "Enhanced KPI..."}]
LLM->>GR: get_recipe("stat-card")
GR-->>LLM: {schema: {...}, recipe: "## When to use\nFor an enhanced KPI..."}
LLM->>WD: widget_display({name: "stat-card", params: {label: "Uptime", value: "99.9%"}})
WD-->>LLM: {widget: "stat-card", id: "w_a3f2"}

The agent can also use WebMCP recipes (.md files) that indicate which widget to use based on the data type. For example, the dashboard-kpi recipe recommends: stat-card for metrics, chart for time series, data-table for details.

The project supports two rendering modes:

ModeTechnologyUsage
Svelte<BlockRenderer> / <WidgetRenderer>SvelteKit apps (flex, viewer)
VanillamountWidget()Framework-free apps, external integration

The BlockRenderer component receives { type, data } and dispatches to the matching Svelte widget:

<script>
import { BlockRenderer } from '@webmcp-auto-ui/ui';
</script>
<BlockRenderer type="stat" data={{ label: 'Revenue', value: '$142K' }} />
  • Simple blocks (stat, kv, list…): data via the data prop
  • Rich blocks (stat-card, data-table…): data via the spec prop
  • Unknown types: renders a [type] placeholder

For framework-free apps:

import { mountWidget } from '@webmcp-auto-ui/core';
const container = document.getElementById('my-widget');
const cleanup = mountWidget(container, 'stat', { label: 'Revenue', value: '$142K' }, [autoui]);
// Clean up on destroy
cleanup?.();

Each block rendered on the canvas auto-registers as a WebMCP tool (via navigator.modelContext). This lets the agent modify widgets already on display:

block_<id>_get -> read the widget's current data
block_<id>_update -> update the widget's data
block_<id>_remove -> remove the widget from canvas

This creates a feedback loop: the agent displays, the user interacts, the agent reacts.

Widgets are organized into 5 groups:

GroupWidgetsUsage
simplestat, kv, list, chart, alert, code, text, actions, tagsBasic blocks, simple data
richstat-card, data-table, timeline, profile, trombinoscope, json-viewer, hemicycle, chart-rich, cards, grid-data, sankey, logComplex visualizations
mediagallery, carousel, mapImages and geography
advancedd3, js-sandboxCustom D3 visualizations, arbitrary code
canvasclear, update, move, resize, styleActions on existing widgets

A key number with optional trend. The simplest and most-used widget.

interface StatBlockData {
label: string; // "Revenue"
value: string; // "$142K"
trend?: string; // "+12.4%"
trendDir?: 'up' | 'down' | 'neutral';
}
{ "type": "stat", "data": { "label": "Revenue", "value": "$142K", "trend": "+12.4%", "trendDir": "up" } }

Ideal for metadata, entity properties, record details.

interface KVBlockData {
title?: string;
rows: [string, string][]; // [["Name", "Dupont"], ["Age", "42"]]
}

A simple bulleted list.

interface ListBlockData {
title?: string;
items: string[];
}

For a quick bar chart. For richer charts (pie, line, area), use chart-rich.

interface ChartBlockData {
title?: string;
bars: [string, number][]; // [["Jan", 10], ["Feb", 20]]
}

Notification with three severity levels.

interface AlertBlockData {
title?: string;
message?: string;
level?: 'info' | 'warn' | 'error';
}

Source code with syntax highlighting.

interface CodeBlockData {
lang?: string; // "python", "sql", "javascript"...
content?: string;
}

Free text with no complex formatting.

interface TextBlockData {
content?: string;
}

A row of clickable buttons. A primary button is visually emphasized.

interface ActionsBlockData {
buttons: { label: string; primary?: boolean }[];
}

A group of badges/labels with optional active state.

interface TagsBlockData {
label?: string;
tags: { text: string; active?: boolean }[];
}

Advanced version of stat with unit, delta, and color variant (success/warning/error/info).

interface StatCardSpec {
label?: string;
value?: unknown;
unit?: string; // "%", "EUR", "km"
delta?: string; // "+12%"
trend?: 'up' | 'down' | 'flat' | {
direction: 'up' | 'down' | 'flat';
value?: string;
positive?: boolean;
};
previousValue?: unknown;
variant?: 'default' | 'success' | 'warning' | 'error' | 'info';
}

Table with configurable columns, click-to-sort, compact and striped modes.

interface DataTableSpec {
title?: string;
columns?: { key: string; label: string; align?: 'left' | 'center' | 'right'; type?: string }[];
rows?: Record<string, unknown>[];
compact?: boolean;
striped?: boolean;
}

Sequence of events with dates and visual statuses.

interface TimelineSpec {
title?: string;
events?: {
date?: string;
title?: string;
description?: string;
status?: 'done' | 'active' | 'pending';
}[];
}

Presentation card with avatar (automatic initials if no URL), structured fields and statistics.

interface ProfileSpec {
name?: string;
subtitle?: string;
avatar?: { src: string; alt?: string };
badge?: { text: string; variant?: string };
fields?: { label: string; value: string }[];
stats?: { label: string; value: string }[];
}

Grid of people with name, subtitle and badge. Ideal for teams, assemblies, panels.

interface TrombinoscopeSpec {
title?: string;
people: { name: string; subtitle?: string; badge?: string; color?: string }[];
columns?: number;
}

Explore a complex JSON structure with fold/unfold by level.

interface JsonViewerSpec {
title?: string;
data: unknown;
maxDepth?: number;
expanded?: boolean;
}

SVG visualization of an assembly’s composition with colors by political group.

interface HemicycleSpec {
title?: string;
groups?: { id: string; label: string; seats: number; color: string }[];
totalSeats?: number;
rows?: number;
}

Advanced chart supporting bar, line, area, pie and donut, with multiple data series.

interface ChartSpec {
title?: string;
type?: 'bar' | 'line' | 'area' | 'pie' | 'donut';
labels?: string[];
data?: { label?: string; values: number[]; color?: string }[];
legend?: boolean;
}

Grid display of results, records, or entities with title, description and tags.

interface CardsSpec {
title?: string;
cards: { title: string; description?: string; subtitle?: string; tags?: string[] }[];
}

Tabular data grid with optional cell highlighting (heatmap).

interface GridDataSpec {
title?: string;
columns?: { key: string; label: string; width?: string }[];
rows: unknown[][]; // row-major: [[1, 2], [3, 4]]
highlights?: { row: number; col: number; color: string }[];
}

Visualizes flows between nodes: votes, co-signatures, journeys.

interface SankeySpec {
title?: string;
nodes?: { id: string; label: string; color?: string }[];
links?: { source: string; target: string; value: number }[];
}

Leaflet map with markers and CARTO dark basemap. The Leaflet module loads dynamically.

interface MapSpec {
title?: string;
center?: { lat: number; lng: number };
zoom?: number; // 1-18
height?: string; // CSS, e.g.: "400px"
markers?: { lat: number; lng: number; label?: string; color?: string }[];
}

D3.js widget with 4 built-in presets: hex-heatmap, radial, treemap, force.

interface D3Spec {
title?: string;
preset: 'hex-heatmap' | 'radial' | 'treemap' | 'force';
data: unknown;
config?: Record<string, unknown>;
}

Secure iframe that executes arbitrary JS code with DOM and fetch access.

interface JsSandboxSpec {
title?: string;
code: string; // JS to execute
html?: string; // initial HTML in div#root
css?: string; // CSS injected in head
height?: string; // CSS height of the iframe
}

Log stream with severity level, timestamp and source.

interface LogViewerSpec {
title?: string;
entries?: {
timestamp?: string;
level?: 'debug' | 'info' | 'warn' | 'error';
message: string;
source?: string;
}[];
}

Image collection with keyboard-navigable lightbox (Escape, arrows).

interface GallerySpec {
title?: string;
images?: { src: string; alt?: string; caption?: string }[];
columns?: number;
}

Slideshow with navigation and optional auto-play.

interface CarouselSpec {
title?: string;
slides?: { src?: string; content?: string; title?: string; subtitle?: string }[];
autoPlay?: boolean;
interval?: number; // ms between slides
}

Displays available recipes as clickable cards. Clicking loads the recipe detail and the agent can then execute it.


These widgets don’t create new blocks — they modify existing ones.

ActionDescriptionParameters
clearClear the entire canvasnone
updateUpdate a block’s data{id, data}
moveMove a block{id, x, y}
resizeResize a block{id, width, height}
styleApply CSS styles{id, styles}

The canvas tool aggregates these 5 actions:

canvas({ action: 'update', id: 'w_a3f2', params: { data: { value: "$155K" } } })
canvas({ action: 'move', id: 'w_a3f2', params: { x: 100, y: 200 } })
canvas({ action: 'clear' })
graph LR
R["WebMCP Recipes"] -->|recommend| W["UI Widgets"]
CT["widget_display()"] -->|instantiates| W
W -->|render on| C["Canvas"]
C -->|exposes| T["Per-block tools<br/>block_id_get/update/remove"]
T -->|let| LLM["Agent LLM<br/>modify widgets"]
  • Recipes: WebMCP recipes tell the agent which widget to use based on the data
  • widget_display: the tool that instantiates a widget on the canvas
  • ToolLayers: widgets are exposed via the autoui server’s WebMcpLayer
  • Canvas: the reactive surface where widgets are rendered and updated
MistakeConsequenceFix
chart for pie/donutchart only does barsUse chart-rich with type: "pie"
Objects in grid-data rowsGrid expects unknown[][]Use data-table for objects
No id on hemicycle groupsClick events breakAlways include id
columns.key doesn’t match rowsEmpty cellskey must match row object keys
LLM-fabricated avatar URLBroken imageNever fabricate URLs — widget shows initials
More than 200 data-table rowsTable truncated silentlyPaginate or filter data first