---
title: "Build a widget"
description: "Create a widget with declared fields and example data, check its code, and show it from an API action or a matching widget condition."
---

:::note[Plan]
Actions are available on the **Starter** plan and above.
:::

Build a widget to show selected data as a card under a reply. You need an agent and permission to edit it. For API-backed data, create an [API connection](/docs/agents/api-connections) to an API that returns JSON.

## Create a widget

1. **Open the builder**

    Go to your agent’s **Build** > **Widgets** and select **Create widget**. A new widget opens to the **Start from a template** gallery. Select a template or **Start from scratch**. Enter a **Widget name**. Names must be unique within this agent. The widget is not created until you save it.

    The top bar shows the name, **Enabled**, **Unsaved changes**, code-check status, action usage, **Save**, and **Widget options**. Turn on **Enabled** to use the widget from its own condition or a Custom API action.

    The editor has four tabs:

    | Tab | Purpose |
    | --- | --- |
    | **Code** | Write the card using the supported components and expressions. |
    | **Functions** | Define what happens when a visitor selects a button or list item. |
    | **States** | Define when sections of the card appear. |
    | **Settings** | Choose when the widget appears on its own. |

    Under **Code**, the bottom panel contains **Fields**, **Default**, and a tab for each named example. The preview is beside the editor on wide screens and below it on smaller screens.

2. **Write the card**

    Replace the code with one expression rooted in `<Card>`. Put text in a `value` prop, rather than between tags. Do not add imports, variable declarations, or a component function.

    ```jsx
    <Card>
      <Title value="Your order" />
      <Text value={`Order ${order.number}`} />
      <Text value={order.status === "Shipped" ? "On its way" : "Preparing"} />
    </Card>
    ```

    Field reads use braces: `value={status}` reads a top-level field, `value={order.status}` reads a nested field, and `value={items[0].name}` reads the first list item. Variables set by functions use the same syntax, such as `value={pickedName}`.

    Use `.map()` to repeat components. Its body must be an expression, with one item parameter and an optional index parameter. Other method calls are not supported.

    ```jsx
    <Card>
      <ListView>
        {items.map((item) => (
          <ListViewItem>
            <Text value={item.name} />
          </ListViewItem>
        ))}
      </ListView>
    </Card>
    ```

    Use template strings to combine text and data, and ternaries to choose a value. Comparisons, `&&`, `||`, and `!` are also supported. See the [component reference](/docs/widgets/components) and [functions and states](/docs/widgets/functions-and-states).

    The editor helps you write the code:

    - **Suggestions**: type `<` to see the components and your state names. Inside a tag, type a letter to see that component's props. Inside a prop's quotes, see its allowed values. Inside `functionName: ""`, see your function names, and inside braces, see your field keys.
    - **Format**: select **Format**, or press Shift+Alt+F in the editor, to fix the indentation and line breaks. Formatting does not change what the widget shows, and it keeps your comments. **Format** is not available while the code has errors.
    - **Component reference**: select **Component reference** under the editor to open the [component reference](/docs/widgets/components) in a new tab.

      </Step>
      <Step title="Declare the fields">

    Go to **Fields** and select **Add field** for each value the card reads. For the order card, use:

    | Field | Type | Description |
    | --- | --- | --- |
    | `order.number` | **Text** | The visitor’s order number. |
    | `order.status` | **Text** | The current delivery status. |

    Types are **Text**, **Number**, **Yes/no**, **Object**, and **List**. Fields have a key, type, and optional description; they do not have a separate label. Use **List** for `items` in the repeated-item example.

    Keys use dot paths. Start with a letter or underscore, followed by letters, digits, or underscores. Later path segments can contain letters, digits, or underscores. Duplicate keys and the segments `__proto__`, `prototype`, and `constructor` are refused.

    Declare only visitor-safe data. An **Object** or **List** field includes its whole value. Function-created variables do not need a data field declaration unless you also want to populate their initial values from action data.

3. **Add preview data**

    Go to **Default** and enter a JSON object. **Fill from fields** replaces it with a starting structure based on your fields.

    ```json title="Default example"
    {
      "order": {
        "number": "Not provided",
        "status": "Preparing"
      }
    }
    ```

    Default values also reach live visitors when neither action inputs nor the API supplies a value. Do not use private customer data as a default.

    Select **Add example** to create a named example. Enter an **Example name**, such as `Shipped`, and its JSON:

    ```json title="Shipped example"
    {
      "order": {
        "number": "A-1234",
        "status": "Shipped"
      }
    }
    ```

    Named examples only affect the preview. They merge over the default example, so you can provide only the values you want to change. Select the **Preview data** pills to switch between **Default** and named examples. **Reset** clears preview variables, dismissal, and preview messages. Changing the preview data or definition also resets it.

    In the preview, **Send message** shows a sample visitor message without starting an agent turn. **Open link** opens the configured URL in a new tab. When a widget includes a **Call API** function, the preview header shows a **Call API result: Success | Failure** toggle. Setting the toggle lets you test both outcomes in the preview without making live network requests. In the **Functions** tab, you can select **Run** to execute the endpoint directly with test inputs and inspect real return fields and responses.

4. **Check and save**

    Check the preview and the code status. **Code checked** means the local parser accepted the code; saving also runs server validation of components, props, functions, and limits.

    Parser errors show a location such as `Line 1, column 1`. These are actual parser messages:

    | Message | What to change |
    | --- | --- |
    | `Check the widget syntax.` | Fix incomplete or malformed markup. |
    | `Widget code must start with <Card>.` | Use a Card as the root component. |
    | `Only .map() is allowed.` | Remove other function or method calls. |
    | `Use one or two simple .map() parameters and an expression body.` | Remove a block body or complex parameters. |
    | `Use Text or Caption with a value prop for text.` | Move text into a component’s value prop. |

    Server validation can show these messages. Server errors do not include parser line and column locations:

    | Message | What to change |
    | --- | --- |
    | `<div> is not a widget component.` | Choose an allowed component. |
    | `<Text> has no prop "className".` | Remove unsupported props. |
    | `<Button> cannot have children.` | Use the label prop. |
    | `Function "pick" is not defined.` | Define the named function in Functions. |
    | `Widget trees allow at most 2000 nodes and 40 levels.` | Reduce the tree’s size or nesting. |

    Select **Save**, or press **⌘+S** on macOS or **Ctrl+S** on other systems. Fix any field, example, or code errors and save again. Invalid example JSON shows `Use a valid JSON object.`.

    If you navigate away with changes, **Leave without saving?** offers **Keep editing** or **Leave page**. Leaving discards unsaved changes.

5. **Choose when the widget appears**

    Make sure **Enabled** is on in the top bar. Then go to **Settings** and write a **When to show** condition if you want the widget to appear on its own. Describe the visitor's goal, such as "When the visitor wants to book a call or see open times." Leave the condition empty if an action should be the only thing that shows the widget. A widget with a condition can appear in the web widget, Public Chat, and Playground. See [Trigger Conditions](/docs/actions/trigger-conditions).

6. **Attach the widget to an action**

    Go to **Build** > **Actions** and open or create a [Custom API action](/docs/actions/custom-api). Select an [API connection](/docs/agents/api-connections), then choose your saved widget in the **Widget** card. The action calls the API and shows the widget after a successful JSON response if the widget is enabled.

    **Create widget** and **Edit widget** open the builder in a new tab. Save there, then return to the action to select it. Save the action and test a conversation in the [Playground](/docs/agents/playground). The action editor’s test request does not render widgets.

The saved widget is available to this agent's actions. A matching conversation can show it under the reply in the web widget, Public Chat, or Playground.

## Start from a template

Templates are starting points stored in the builder. They use no AI and no credits.

1. **Open the template gallery**

    For a new widget, go to **Build** > **Widgets** and select **Create widget**. The **Start from a template** gallery shows six template cards and **Start from scratch**.

    For an existing widget, open it and select **Widget options** > **Start from a template** in the top bar. The gallery opens in a dialog.

2. **Preview and apply a template**

    Select a card to preview it, then select **Use template**. For an existing widget, the builder asks **Replace this widget’s code, fields, examples, functions and states?**. Select **Replace widget** to confirm.

    Applying a template fills in the code, fields, examples, functions, and states. If a new widget still has the default name, its name becomes the template title.

3. **Connect the template to your data**

    Go to **Fields** to check the field names. Adjust the widget and save it, then [attach it to a Custom API action](/docs/actions/custom-api) or set its **When to show** condition. Match the API response to the template's fields. Test the result in the [Playground](/docs/agents/playground).

The widget now uses the template as its starting point. You can edit its code, fields, examples, functions, and states.

| Template | What it shows | Fields it expects |
| --- | --- | --- |
| **Product list** | Search results with image, price, stock and a **View** link. | `query`, `count`, `products` |
| **Order tracker** | Order status, items, a delay notice and tracking. | `order` |
| **Pricing plans** | Plans with price, key features and a **Choose plan** link. | `plans` |
| **Appointment slots** | Pick a time, then book it in the chat. | `date`, `slots`, `picked` |
| **Contact card** | Support hours, email and phone links, talk to a person. | `hours`, `replyTime`, `online`, `email`, `phone`, `contactUrl`, `callUrl` |
| **Quick replies** | A short question with buttons that answer for the visitor. | `question`, `replies` |

## Delete a widget

1. **Remove action references**

    Check the widget’s **Used by** names. Remove or replace its selection in every action, including disabled actions, and save those actions. Deletion is refused while any action uses the widget; the refusal names the actions.

2. **Delete the saved widget**

    Go to **Build** > **Widgets**, open the widget’s options, and select **Delete widget**. You can also use **Widget options** in the builder. Select **Delete** to confirm. Replies that already displayed it keep their copy.

## Limits

| Item | Limit |
| --- | --- |
| Widget name | 100 characters; unique within the agent |
| Source | 20,000 characters |
| Component and expression tree | 2,000 nodes and 40 levels; root must be Card |
| Literal strings in the tree | 2,000 characters each |
| Fields | 50 |
| Field description | 500 characters |
| Named examples | 10; names up to 60 characters |
| States | 20; names match `^[A-Z][A-Za-z0-9]{0,39}$` |
| Groups per state | 10 |
| Conditions per group | 20; comparison value up to 500 characters |
| Functions | 20; names match `^[A-Za-z_][A-Za-z0-9_]{0,63}$` |
| Open link URL | 2,048 characters; starts with `http://` or `https://` |
| Function message | 4,000 characters |
| Variable value template | 1,000 characters |
