Getting Started
Quickstart
Build the recommended ToonUI flow with React and AI SDK.
This page builds the happy path:
- configure the active ToonUI catalog
- generate the model prompt on the server
- stream model output with AI SDK
- render markdown plus ToonUI in React
- send reply/submit interactions back to the chat loop
1. Share the component keys
Create a small shared file for the standard ToonUI components your app supports.
export const toonComponentKeys = ['text', 'card', 'form', 'field', 'button', 'table'] as const;Do not invent component names here. These must be standard components supported by the ToonUI parser.
2. Server: create the protocol
import { openai } from '@ai-sdk/openai';
import { convertToModelMessages, streamText, type UIMessage } from 'ai';
import { createToonProtocol } from '@toon-ui/core';
import { toonComponentKeys } from '@/lib/toon-components';
const toon = createToonProtocol({
components: toonComponentKeys,
});
const system = [
toon.prompt,
'You are helping users compare products.',
'Use ToonUI when structured UI reduces friction.',
'Available tools:',
'- searchProducts(query)',
].join('\n\n');
export async function POST(request: Request) {
const { messages } = (await request.json()) as { messages: UIMessage[] };
const result = streamText({
model: openai('gpt-4o-mini'),
system,
messages: await convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
}3. Client: create the React runtime
'use client';
import { ToonMessage, createToonReactRuntime } from '@toon-ui/react';
import { MessageResponse } from '@/components/ai-elements/message';
const toon = createToonReactRuntime({
components: {
text: TextComponent,
card: CardComponent,
form: FormComponent,
field: FieldComponent,
button: ButtonComponent,
table: TableComponent,
},
});
export function AssistantMessage({
content,
append,
}: {
content: string;
append: (message: unknown) => void;
}) {
return (
<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))}
/>
);
}4. What the model can emit
Because the server catalog only enabled text, card, form, field, button, and table, the prompt teaches only that subset.
Example valid output:
Here are the best matches.
```txt
card "Recommended product":
text "Coca-Cola 400ml has strong availability."
button primary "View details" reply="view product details"
```If the model emits a component outside the active catalog, validation rejects it instead of letting unsupported UI render silently.
5. Next step
Read the full Vercel AI SDK guide.