# Code editor

> Write and refine widgets by hand using the JSX-like markup language, a typed data schema, example data for the live preview, and interactive functions.

The code editor gives you full control over a widget. You write the UI in a JSX-like markup language over Bookbag's component library, declare the data the widget expects, provide example data for the live preview, and define the functions that run when a customer interacts.

## The four parts of a widget

| Part | What it does |
| --- | --- |
| Code | The JSX-like markup — the layout and components the widget renders. |
| Schema | The typed data the widget expects, as a Zod schema with a JSON mirror. |
| Example data | Sample values used to render the preview and to fall back on at runtime. |
| Functions & states | Behavior for interactive elements, and conditional visibility (e.g. a "submitted" state). |

## The markup language

Widget markup looks like JSX but is **not** React — it is parsed and evaluated by a safe interpreter (no `eval`). You can reference data fields, use ternaries and `.map()` to repeat over arrays, and compose the built-in components. Here is a minimal lead form:

```jsx
<Card size="md" asForm>
  <Col gap={4}>
    <Title value={title} size="lg" weight="medium" />
    <Text value={subtitle} size="sm" color="secondary" />
    <Divider />
    <Col gap={2}>
      <Label value="Work email" fieldName="email" />
      <Input name="email" inputType="email" placeholder="you@example.com" required />
    </Col>
    <Button label="Submit" submit variant="solid" color="primary" block
      onClickAction={{ functionName: "submitLead" }} />
  </Col>
</Card>
```

> **REPEAT OVER A LIST WITH .MAP():** Render one component per item in an array — exactly like JSX — to build carousels, lists, and tables from data.

```jsx
<ListView orientation="horizontal" gap={2}>
  {products.map((product) => (
    <ListViewItem key={product.id}>
      <Col gap={2} width={240}>
        <Image src={product.image} height={190} fit="cover" radius="md" />
        <Title value={product.name} size="xs" weight="semibold" />
        <Text value={product.price} size="md" weight="medium" />
        <Button label="Add to cart" block
          onClickAction={{ functionName: "addToCart",
            additionalInputs: { productId: product.id } }} />
      </Col>
    </ListViewItem>
  ))}
</ListView>
```

## The data schema

The schema declares what data the widget needs. It is written as a Zod schema and mirrored as JSON, so the AI builder, your actions, and the agent all agree on the shape. The `.describe()` text helps the model populate fields correctly at runtime.

```js
z.object({
  title: z.string().describe("Heading"),
  subtitle: z.string().describe("Subheading"),
  products: z.array(z.object({
    id: z.string(),
    name: z.string(),
    price: z.string(),
    image: z.string(),
  })).describe("Products to display"),
})
```

## Functions: making it interactive

Buttons and inputs trigger **functions**. A function has a type that decides what happens when it runs:

| Function type | What it does |
| --- | --- |
| `message` | Sends a message back into the chat (e.g. a templated lead summary). |
| `link` | Opens a URL. |
| `setVariables` | Updates the widget's own data — used with states to show/hide sections. |
| `dismiss` | Closes the widget. |
| `server` / `client` | Calls your API (server) or delegates to the host (client) for custom behavior. |

```js
{
  submitLead: {
    type: "message",
    messageTemplate: "New lead from {{name}} ({{email}})",
    onExecute: { action: "dismiss" }
  }
}
```

## States: conditional UI

A **state** shows or hides part of the widget based on its data. A common pattern is a form that flips to a confirmation after submit: wrap the form in a `FormView` state and the thank-you in a `Submitted` state, each shown conditionally on a data field.

> **INFO:** States are written as tag wrappers whose name matches a declared state (for example `<Submitted>…</Submitted>`). The renderer shows only the states whose conditions are met by the current data.

## Live preview and example data

The editor renders a live preview from your **example data**, so you see the widget exactly as a customer would — themed light or dark to match the chat. Add **named examples** to preview specific states (for instance, an example with `submitted: true` to see the confirmation view).

## Import, export, and duplicate

You can **duplicate** any widget to branch from a working version, **export** a widget to move it between agents or workspaces, and **import** one back in. Templates are just exported widgets you can duplicate and edit.

## What's next

- [Components](/docs/widgets/components) — The complete component reference.
- [AI widget builder](/docs/widgets/ai-builder) — Generate a first draft to refine here.
- [Custom action](/docs/actions/custom-action) — Render the widget from an action.
