Skip to main content
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:
{
  "version": "0.2",
  "view": { ... },
  "defs": { ... },
  "state": {
    "count": 42,
    "name": "Alice"
  }
}
KeyTypeDescription
versionstringProtocol version (currently "0.2")
viewComponentThe root component tree to render
defsobjectOptional map of template name to component subtree (see Define/Use below)
stateobjectOptional 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:
{
  "type": "Badge",
  "label": "Active",
  "variant": "success"
}
All components share one optional base field:
FieldTypeDescription
cssClassstringAdditional Tailwind CSS classes
Container components (Row, Column, Card, etc.) also have a children array of nested components. For conditional rendering, use the 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:
{
  "action": "toolCall",
  "tool": "get_weather",
  "arguments": { "city": "{{ city }}" }
}
Action fields accept a single action, an array of actions (executed sequentially), or null:
{
  "type": "Button",
  "label": "Submit",
  "onClick": [
    { "action": "setState", "key": "loading", "value": true },
    { "action": "toolCall", "tool": "process" }
  ]
}
Available action types:
ActionDiscriminatorDescription
SetStatesetStateSet a client-side state variable
ToggleStatetoggleStateFlip a boolean state variable
ToolCalltoolCallCall a server-side tool
SendMessagesendMessageSend a message to the chat
UpdateContextupdateContextSilently update model context
OpenLinkopenLinkOpen a URL
ShowToastshowToastDisplay 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.).
{
  "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:
{
  "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:
{ "$ref": "user-card" }
With scoped data — wrap the $ref in a State node to provide interpolation values:
{
  "type": "State",
  "state": { "name": "Alice", "role": "Engineer" },
  "children": [{ "$ref": "user-card" }]
}
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.