Skip to content

Use existing widgets

WebMCP Auto-UI ships with 26+ ready-to-use widget types. This tutorial shows you how to use them, customize them, and make them interact with each other. No need to create anything — everything is already there.

Discover and use the native widgets to build rich interfaces without writing custom components.

  • The boilerplate is installed (see Getting started)
  • Basic Svelte 5 knowledge ($state, {#each})

An interactive dashboard using stat, chart-rich, data-table, timeline, and profile — with dynamic updates and interaction handling.


WidgetDescriptionTypical use
statKey statistic (KPI)Number with trend
kvKey-value pairsMetadata, properties
listOrdered listText items
chartSimple bar chartQuick comparisons
alertNotification/alertImportant messages
codeCode block with syntax highlightingCode snippets
textText paragraphWritten content
actionsAction buttonsCalls to action
tagsBadges/tagsFilters, categories
WidgetDescriptionTypical use
stat-cardEnhanced KPI with delta and colorFinancial dashboard
data-tableSortable table with columnsData lists
timelineEvent chronologyProject history
profileProfile card with avatarContact page
trombinoscopePortrait gridTeams
json-viewerInteractive JSON treeDebug, API
hemicycleParliamentary compositionPolitics
chart-richMulti-series (bar, line, area, pie)Comparative analysis
cardsCard gridCatalogs
grid-dataGrid with cell highlightsMatrices
sankeyFlow diagramFinancial flows
mapInteractive Leaflet mapGeolocation
logLog streamMonitoring
galleryImage gallery with lightboxPortfolios
carouselSlide carouselPresentations
d3D3.js visualizationsAdvanced charts
js-sandboxCustom JavaScript sandboxInteractive code
graph TD
subgraph Simple
stat[stat]
kv[kv]
list[list]
chart[chart]
alert[alert]
code[code]
text[text]
actions[actions]
tags[tags]
end
subgraph Rich
statcard[stat-card]
datatable[data-table]
timeline[timeline]
profile[profile]
chartrich[chart-rich]
map[map]
gallery[gallery]
sankey[sankey]
end
BlockRenderer --> Simple
BlockRenderer --> Rich

BlockRenderer is the entry point for displaying any widget. It automatically resolves the Svelte component matching the widget type:

<script lang="ts">
import { BlockRenderer } from '@webmcp-auto-ui/ui';
import { canvas } from '@webmcp-auto-ui/sdk/canvas';
</script>

BlockRenderer takes three main props:

  • type: the widget name ('stat', 'data-table', etc.)
  • data: the data in the format expected by the widget
  • id: (optional) unique identifier for interactions

The canvas store provides methods to add widgets:

<script lang="ts">
function addStatWidget() {
canvas.addWidget('stat', {
label: 'Total sales',
value: '$12,450',
trend: '+12%',
trendDir: 'up',
});
}
function addChartWidget() {
canvas.addWidget('chart', {
title: 'Monthly sales',
bars: [
['January', 120],
['February', 190],
['March', 150],
],
});
}
function addTableWidget() {
canvas.addWidget('data-table', {
title: 'Active customers',
columns: [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'status', label: 'Status' },
],
rows: [
{ name: 'Alice Dupont', email: 'alice@example.com', status: 'Active' },
{ name: 'Bob Martin', email: 'bob@example.com', status: 'Suspended' },
{ name: 'Charlie Lenoir', email: 'charlie@example.com', status: 'Active' },
],
});
}
</script>
<button onclick={addStatWidget}>Add KPI</button>
<button onclick={addChartWidget}>Add chart</button>
<button onclick={addTableWidget}>Add table</button>

Checkpoint: click each button and verify the corresponding widget appears.


Loop through the canvas blocks and render them with BlockRenderer:

<div class="widgets-grid">
{#each canvas.blocks as block (block.id)}
<BlockRenderer
id={block.id}
type={block.type}
data={block.data}
/>
{/each}
</div>
<style>
.widgets-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1rem;
padding: 1rem;
}
</style>

<script lang="ts">
canvas.addWidget('stat-card', {
label: 'Conversion rate',
value: '3.2',
unit: '%',
trend: 'up',
delta: '+0.5%',
variant: 'success', // 'success' | 'warning' | 'danger' | 'info'
});
</script>

5 chart types in a single widget:

<script lang="ts">
canvas.addWidget('chart-rich', {
title: 'Quarterly performance',
type: 'bar', // 'bar' | 'line' | 'area' | 'pie' | 'donut'
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
data: [
{ label: 'Sales', values: [120, 190, 150, 180], color: '#3b82f6' },
{ label: 'Profit', values: [80, 140, 110, 150], color: '#10b981' },
],
});
</script>
<script lang="ts">
canvas.addWidget('timeline', {
title: 'Project history',
events: [
{ title: 'Kickoff', date: '2024-01-15', status: 'done', description: 'Project launched' },
{ title: 'First release', date: '2024-02-28', status: 'done', description: 'MVP delivered' },
{ title: 'Optimizations', date: '2024-04-01', status: 'active', description: 'In progress' },
{ title: 'Public launch', date: '2024-05-01', status: 'pending', description: 'Scheduled' },
],
});
</script>
<script lang="ts">
canvas.addWidget('profile', {
name: 'Alice Dupont',
subtitle: 'Senior Developer',
badge: { text: 'Online', variant: 'success' },
fields: [
{ label: 'Team', value: 'Backend' },
{ label: 'Location', value: 'Paris, France' },
{ label: 'Since', value: '2021' },
],
stats: [
{ label: 'Projects', value: '12' },
{ label: 'Contributors', value: '45' },
],
});
</script>
<script lang="ts">
canvas.addWidget('map', {
title: 'Office locations',
center: { lat: 46.6, lng: 2.3 },
zoom: 6,
markers: [
{ lat: 48.86, lng: 2.35, label: 'Paris (HQ)' },
{ lat: 43.60, lng: 1.44, label: 'Toulouse' },
{ lat: 45.76, lng: 4.84, label: 'Lyon' },
],
});
</script>
<script lang="ts">
canvas.addWidget('code', {
lang: 'typescript',
content: `interface User {
id: number;
name: string;
email: string;
}`,
});
</script>
<script lang="ts">
canvas.addWidget('json-viewer', {
title: 'Data structure',
data: {
user: { id: 123, name: 'Alice', tags: ['admin', 'developer'] },
},
maxDepth: 3,
expanded: true,
});
</script>

Some widgets emit events when the user interacts with them. Use the oninteract prop to capture them:

<BlockRenderer
id={block.id}
type={block.type}
data={block.data}
oninteract={(type, action, payload) => {
console.log(`Widget ${type} -- action: ${action}`, payload);
}}
/>

Events emitted by widgets:

WidgetActionPayload
data-tablerowclickRow object
timelineeventclickEvent object
cardscardclickCard object
galleryimageclick{image, index}
tagstagclick{tag, index}
actionsclick{action, index}

Modify a widget after creation using canvas methods:

<script lang="ts">
function updateStat(blockId: string) {
canvas.updateBlock(blockId, {
value: '$15,000',
trend: '+20%',
trendDir: 'up',
});
}
function removeWidget(blockId: string) {
canvas.removeBlock(blockId);
}
function clearAll() {
canvas.clearBlocks();
}
</script>

Wrap your widgets in a ThemeProvider to apply a consistent theme:

<script lang="ts">
import { ThemeProvider } from '@webmcp-auto-ui/ui';
</script>
<ThemeProvider defaultMode="light" overrides={{
'color-accent': '#2d6a4f',
'color-bg': '#f4f1eb',
}}>
<div class="widgets-grid">
{#each canvas.blocks as block (block.id)}
<BlockRenderer id={block.id} type={block.type} data={block.data} />
{/each}
</div>
</ThemeProvider>

All native widgets respect theme tokens. Changing color-accent updates the accent color across all charts, progress bars, and buttons.


<script lang="ts">
import { BlockRenderer } from '@webmcp-auto-ui/ui';
import { canvas } from '@webmcp-auto-ui/sdk/canvas';
import { onMount } from 'svelte';
function buildDashboard(data: any) {
canvas.clearBlocks();
canvas.addWidget('stat-card', {
label: 'Monthly revenue',
value: data.revenue.toLocaleString('en-US'),
unit: '$',
trend: 'up',
variant: 'success',
});
canvas.addWidget('chart-rich', {
title: 'Trend',
type: 'line',
labels: data.months,
data: [{ label: 'Revenue', values: data.values }],
});
canvas.addWidget('data-table', {
title: 'Latest transactions',
columns: [
{ key: 'date', label: 'Date' },
{ key: 'amount', label: 'Amount' },
{ key: 'status', label: 'Status' },
],
rows: data.transactions,
});
}
onMount(async () => {
const data = await fetch('/api/dashboard').then(r => r.json());
buildDashboard(data);
});
</script>
<div class="dashboard">
{#each canvas.blocks as block (block.id)}
<BlockRenderer id={block.id} type={block.type} data={block.data} />
{/each}
</div>
sequenceDiagram
participant Page
participant API
participant Canvas
participant BR as BlockRenderer
Page->>API: GET /api/dashboard
API-->>Page: {revenue, months, values, transactions}
Page->>Canvas: clearBlocks()
Page->>Canvas: addWidget('stat-card', ...)
Page->>Canvas: addWidget('chart-rich', ...)
Page->>Canvas: addWidget('data-table', ...)
Canvas-->>BR: reactive blocks
BR-->>Page: widgets rendered

ProblemLikely causeSolution
Widget shows as raw JSONUnrecognized typeCheck spelling (e.g., data-table, not datatable)
Empty tablerows missing or emptyEnsure rows is an array of objects
Invisible chartlabels or data missingBoth properties are required for chart-rich
Theme not appliedNo ThemeProviderWrap your widgets in a <ThemeProvider>