Skip to main content

Create a widget

This guide walks you through building a widget from scratch — from opening the editor to saving a finished, testable widget.

Step 1: Open Widget Builder

In your bot, go to AI Agent → Widget Builder.

You'll land on the start screen with three ways to begin:

  • Start from scratch — opens the editor with a default starter widget.
  • Pick from library — browse predefined widgets that you can customize.
  • Generate with AI — describe what you want in natural language and let the platform produce an initial draft.

If you've already saved widgets on this bot, you'll also see a Saved widgets entry — useful for editing existing ones.

Tip for first-timers: start from a library widget close to what you want and modify it. It's faster than from scratch and you'll learn the structure by editing.

Step 2: Understand the editor layout

The editor has four panes you'll use:

PaneWhereWhat it does
CodeLeftThe widget's JSX source. This is the UI.
SchemaBelow codeThe inputs the widget expects when an agent shows it.
Default stateA tab next to schemaInitial values for any local UI state.
PreviewRightLive render of your widget.

Drag the dividers to resize panes if you need more space.

Step 3: Write the widget UI

Click into the Code pane and edit the JSX. The platform supports a curated set of UI primitives — open the Components Library drawer (icon in the editor toolbar) to see what's available and to drop in tested patterns like buttons, inputs, and layouts.

As you type, the Preview pane re-renders continuously. If something breaks, the inline diagnostics will point at the line.

Best practices for the code pane:

  • Start with one component and get it rendering before adding more.
  • Use the components library — hand-rolling primitives is slower and less reliable.
  • Save your work early and often. There's no auto-save.
  • If preview shows an error, read it carefully — most "broken widgets" are typos or unsupported tags.

Step 4: Define the schema

Open the Schema pane. The schema is the contract between your widget and the agent: it lists every input your widget expects.

Example for a booking widget:

{
"type": "object",
"properties": {
"userName": { "type": "string" },
"availableSlots": {
"type": "array",
"items": { "type": "string" }
}
},
"required": ["userName", "availableSlots"]
}

When an agent shows this widget, it must provide values for userName and availableSlots.

Best practices for schema:

  • Keep it flat. A schema with deeply nested fields is harder to bind in flows and harder to debug.
  • Mark required inputs explicitly. Without required, the flow node won't catch missing inputs at design time.
  • Use enum for known sets ("status: ['pending','approved','rejected']") — free validation, clearer intent.
  • Don't ask for data the widget doesn't actually use. Schema bloat slows everyone down.

Step 5: Set the default state

Default state is for the widget's own working memory — values it changes as the user interacts (the currently selected option, the active step, etc.). Put a JSON object here:

{
"selectedSlot": null,
"step": "pick-time"
}

Best practices for default state:

  • Keep it shallow. Flat keys are easiest to debug and cheapest to serialize.
  • Don't duplicate schema inputs. If userName is in the schema, don't also store it in state.
  • Don't put agent-level data here. The user's identity, account info, or session belong in agent state, not widget state.

Step 6: Define expected output

Open the Output pane. This describes what your widget returns when the user is done — what the agent will read in the next turn.

{
"selectedSlot": "string",
"confirmed": "boolean"
}

Best practices for output:

  • Match output keys to flow variables. If the next flow node expects selectedSlot, name your output selectedSlot. Renaming downstream is painful.
  • Emit on submit, not on every change. Continuous emissions make traces noisy and rack up turn counts.
  • Keep output flat and named clearly. Agents read these by key in subsequent prompts.

Step 7: Preview and iterate

The Preview pane shows your widget rendered against your default state. Click around. Try every interaction. If something doesn't work, edit the code and watch the preview update.

Tip: if your preview suddenly shows a stale render after switching widgets, give it a moment — the editor briefly compiles in the background.

Step 8: Save the widget

When you're happy, click Save. A modal opens. Provide:

  • Name — what you'll see in saved widgets and in flow node pickers. Pick something descriptive: BookingForm — APAC beats Form2.

The platform auto-generates a stable slug (e.g. widget_TkpB0njZg9) used internally. You'll see it in the URL once saved.

After save, the Update button stays disabled until you make a fresh edit — so you don't accidentally overwrite the same content twice.

Step 9: Find it again

Saved widgets appear in:

  • Widget Builder → Saved — the list of every widget on this bot.
  • The Rich Media picker inside flow nodes — for wiring the widget into a flow.

Editing tips

  • Production bots are read-only by default. If clicks are doing nothing, check the environment banner — you're probably looking at production.
  • Renaming a widget keeps the slug stable. Flows that already reference the widget keep working.
  • Be careful changing schema after wiring. If you change required inputs, every flow node that calls this widget will need updating. Pre-warn flow owners before you change a live widget's schema.

Best practices recap

  1. Start from a library widget, not a blank file.
  2. Keep schema flat and required fields explicit.
  3. Don't duplicate data between schema and state.
  4. Match output keys to the names downstream flows expect.
  5. Save early — there's no autosave.
  6. Use the components library; don't reinvent primitives.

Continue to: Use widgets in agents.