---
title: Overview
description: Wire format for the Prefab UI protocol.
---

The Prefab protocol defines the JSON wire format exchanged between a server (Python SDK) and a client (renderer). A server returns a `UIResponse` that serializes to the envelope below. The renderer parses it, resolves templates, interpolates state, and renders the component tree.

## Envelope

Every response is a JSON object with clean top-level keys:

```json
{
  "version": "0.2",
  "view": { ... },
  "defs": { ... },
  "state": {
    "count": 42,
    "name": "Alice"
  }
}
```

| Key | Type | Description |
|-----|------|-------------|
| `version` | `string` | Protocol version (currently `"0.2"`) |
| `view` | `Component` | The root component tree to render |
| `defs` | `object` | Optional map of template name to component subtree (see Define/Use below) |
| `state` | `object` | Optional client-side state, accessible via `{{ key }}` interpolation |

State keys must not start with `$` (reserved for interpolation builtins like `$event` and `$error`).

## Components

Every component is a JSON object with a `type` discriminator:

```json
{
  "type": "Badge",
  "label": "Active",
  "variant": "success"
}
```

All components share one optional base field:

| Field | Type | Description |
|-------|------|-------------|
| `cssClass` | `string` | Additional Tailwind CSS classes |

Container components (Row, Column, Card, etc.) also have a `children` array of nested components.

For conditional rendering, use the [Condition](/protocol/condition) component with `cases` and an optional `else` branch.

## Actions

Actions define what happens on user interaction. They appear in event handler fields like `onClick`, `onChange`, and `onSubmit`. An action uses an `action` discriminator instead of `type`:

```json
{
  "action": "toolCall",
  "tool": "get_weather",
  "arguments": { "city": "{{ city }}" }
}
```

Action fields accept a single action, an array of actions (executed sequentially), or `null`:

```json
{
  "type": "Button",
  "label": "Submit",
  "onClick": [
    { "action": "setState", "key": "loading", "value": true },
    { "action": "toolCall", "tool": "process" }
  ]
}
```

Available action types:

| Action | Discriminator | Description |
|--------|--------------|-------------|
| [SetState](/protocol/set-state) | `setState` | Set a client-side state variable |
| [ToggleState](/protocol/toggle-state) | `toggleState` | Flip a boolean state variable |
| [CallTool](/protocol/tool-call) | `toolCall` | Call a server-side tool |
| [SendMessage](/protocol/send-message) | `sendMessage` | Send a message to the chat |
| [UpdateContext](/protocol/update-context) | `updateContext` | Silently update model context |
| [OpenLink](/protocol/open-link) | `openLink` | Open a URL |
| [ShowToast](/protocol/show-toast) | `showToast` | Display a toast notification |

## Interpolation

All string properties support `{{ key }}` placeholders that resolve against client-side state at render time. The special value `{{ $event }}` captures the triggering interaction's value (slider position, input text, checkbox state, etc.).

```json
{
  "type": "P",
  "content": "Hello, {{ name }}! You have {{ count }} items."
}
```

## Define / Use (Templates)

Templates let you define a component subtree once and reference it multiple times with different data.

**Defining a template** — entries in `defs` map a name to a component subtree:

```json
{
  "defs": {
    "user-card": {
      "type": "Card",
      "children": [
        { "type": "CardTitle", "content": "{{ name }}" },
        { "type": "CardDescription", "content": "{{ role }}" }
      ]
    }
  }
}
```

**Using a template** — a `$ref` node references a definition by name:

```json
{ "$ref": "user-card" }
```

**With scoped data** — add `let` bindings to the `$ref` node:

```json
{
  "$ref": "user-card",
  "let": { "name": "Alice", "role": "Engineer" }
}
```

The renderer resolves `$ref` nodes by looking up the definition and rendering it with the current interpolation context. Circular references are detected and short-circuited.
