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
| API | Input | Output | Use when |
|---|---|---|---|
toon.events.reply(value, context?) | reply string + optional context | ReplyPayload | creating a button-style interaction payload |
toon.events.submit(intent, values?) | submit intent + values | SubmitPayload | creating a form-style interaction payload |
toon.messages.toContent(payload) | reply or submit payload | compact string | sending protocol text to a model |
toon.messages.toDisplayContent(payload) | reply or submit payload | human-readable string | showing the interaction in chat history |
toon.messages.toModelMessage(payload) | reply or submit payload | model-facing object | reinjecting into a custom model loop |
toon.messages.toUIMessage(payload) | reply or submit payload | UI-message-shaped object | appending 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>;
};| Field | Meaning |
|---|---|
kind | Discriminator for reply interactions. |
eventId | Generated id for this interaction. |
source | Source primitive; currently button. |
component | Component that produced the reply; currently button. |
value | User intent value. |
line | Optional source line from generated ToonUI. |
context | Optional 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;
};| Field | Meaning |
|---|---|
kind | Discriminator for submit interactions. |
eventId | Generated id for this interaction. |
source | Source primitive; currently form. |
intent | App-facing submit intent. |
formTitle | Human-facing form title. |
values | Submitted values. |
line | Optional 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: buttonSubmit output shape:
ui_submit:
eventId: submit_abc123
intent: create_product
formTitle: create_product
name: "Coca-Cola"
price: 2500Use 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.
Recommended React usage
<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.