ToonUI
API Reference

Events and Messages

Detailed reference for reply/submit payloads and how ToonUI converts interactions back into your host loop.

Events and messages are the bridge between rendered ToonUI and your application loop.

A rendered button or form does not execute business logic. It creates structured user intent. Your app decides what happens next.

API summary

APIInputOutputUse when
toon.events.reply(value, context?)reply string + optional contextReplyPayloadcreating a button-style interaction payload
toon.events.submit(intent, values?)submit intent + valuesSubmitPayloadcreating a form-style interaction payload
toon.messages.toContent(payload)reply or submit payloadcompact stringsending protocol text to a model
toon.messages.toDisplayContent(payload)reply or submit payloadhuman-readable stringshowing the interaction in chat history
toon.messages.toModelMessage(payload)reply or submit payloadmodel-facing objectreinjecting into a custom model loop
toon.messages.toUIMessage(payload)reply or submit payloadUI-message-shaped objectappending to AI SDK-style UI state

Reply payloads

Reply payloads represent button-like user intent.

const payload = toon.events.reply('Open product details', {
  productId: 123,
});

Returns:

type ReplyPayload = {
  kind: 'ui_reply';
  eventId: string;
  source: 'button';
  component: 'button';
  value: string;
  line?: number;
  context?: Record<string, SubmitValue>;
};
FieldMeaning
kindDiscriminator for reply interactions.
eventIdGenerated id for this interaction.
sourceSource primitive; currently button.
componentComponent that produced the reply; currently button.
valueUser intent value.
lineOptional source line from generated ToonUI.
contextOptional app metadata.

Submit payloads

Submit payloads represent form submissions.

const payload = toon.events.submit('create_product', {
  name: 'Coca-Cola',
  price: 2500,
});

Returns:

type SubmitPayload = {
  kind: 'ui_submit';
  eventId: string;
  source: 'form';
  intent: string;
  formTitle: string;
  values: Record<string, SubmitValue>;
  line?: number;
};
FieldMeaning
kindDiscriminator for submit interactions.
eventIdGenerated id for this interaction.
sourceSource primitive; currently form.
intentApp-facing submit intent.
formTitleHuman-facing form title.
valuesSubmitted values.
lineOptional source line from generated ToonUI.

toContent

Serializes payloads into compact protocol text.

const content = toon.messages.toContent(payload);

Reply output shape:

ui_reply:
  eventId: reply_abc123
  value: Open product details
  source: button
  component: button

Submit output shape:

ui_submit:
  eventId: submit_abc123
  intent: create_product
  formTitle: create_product
  name: "Coca-Cola"
  price: 2500

Use this when you need the raw text protocol.

toDisplayContent

Creates human-readable content for UI history.

const display = toon.messages.toDisplayContent(payload);

For replies, this is the reply value.

For submits, this is the form title plus submitted values. If the submit payload includes the original form node, field labels are used instead of raw field names.

toModelMessage

Creates a model-facing message object.

const message = toon.messages.toModelMessage(payload);

Returns:

type ToonModelMessage<TPayload> = {
  role: 'user';
  kind: TPayload['kind'];
  content: string;
  displayContent: string;
  payload: TPayload;
};

Use it when the interaction should become the next user turn in your model loop.

toUIMessage

Creates a UI-message-shaped object.

const message = toon.messages.toUIMessage(payload);

Returns:

type ToonUIMessage<TPayload> = {
  id: string;
  role: 'user';
  parts: [{ type: 'text'; text: string }];
  metadata: {
    displayContent: string;
    kind: TPayload['kind'];
  };
};

Use it when your chat store expects message objects with parts and metadata.

<ToonMessage
  content={content}
  runtime={toon}
  renderMarkdown={(markdown) => <MessageResponse>{markdown}</MessageResponse>}
  renderError={(error) => <MyError details={error.details} />}
  onReply={(payload) => append(toon.messages.toUIMessage(payload))}
  onSubmit={(payload) => append(toon.messages.toUIMessage(payload))}
/>

Boundary reminder

A reply or submit payload is intent, not execution.

Do not let the prompt decide business behavior. Your app owns tools, permissions, persistence, and side effects.

On this page