# Widget Templates Minimal HTML scaffolds for the common widget shapes. Copy, fill in, ship. All templates assume the apps-SDK helper is available at an ESM CDN. They're intentionally framework-free — widgets render in a fresh iframe each time, so React/Vue hydration cost usually isn't worth it for something this small. --- ## The render helper Ten lines of string templating. Good enough for almost every case. ```typescript import { readFileSync } from "node:fs"; import { join } from "node:path"; const TEMPLATE_DIR = join(import.meta.dirname, "../widgets"); export function renderWidget(name: string, data: unknown): string { const tpl = readFileSync(join(TEMPLATE_DIR, `${name}.html`), "utf8"); return tpl.replace( "__DATA__", JSON.stringify(data).replace(/__DATA__`. The `<` escape prevents `` injection. --- ## Picker (single-select list) ```html ``` **Data shape:** `{ items: [{ id, label, sub? }] }` **Result shape:** `{ id }` --- ## Confirm dialog ```html

``` **Data shape:** `{ message, confirmLabel? }` **Result shape:** `{ confirmed: boolean }` --- ## Progress (long-running) ```html

Starting…

``` The server pushes updates via the transport's notification channel targeting this widget's session. See `apps-sdk-messages.md` for the server-side push. --- ## Display-only (chart / preview) Display widgets don't need `submit()` — they render and sit there. Return a text summary **alongside** the widget so Claude can keep reasoning: ```typescript return { content: [ { type: "text", text: "Revenue is up 12% MoM. Chart rendered below." }, { type: "resource", resource: { uri: "ui://widgets/chart", mimeType: "text/html+skybridge", text: renderWidget("chart", data) } }, ], }; ```