React UI
Examples
Recipe-style examples for building @anvia/react-ui surfaces.
These examples show complete composition patterns rather than isolated primitives. Use them as starting points for app-owned chat, completion, tool-call, and review experiences.
Recipes
- Full chat surface: thread layout, suggestions, default message rendering, composer, and scroll controls.
- Message rendering: default renderer, Markdown, custom code blocks, attachments, tool cards, and actions.
- Images, selection, and thread list: image attachment previews, quote-from-selection, and app-owned conversation history.
- Styling recipe: complete Vanilla CSS and Tailwind chat styling.
- Composer attachments: file picker, direct file input, dropzone, controlled attachments, and custom attachment rows.
- Composer controls: model selectors, reasoning effort, request shaping, and custom submit metadata.
- Composer triggers: people mentions, slash commands, variables, custom trigger menus, and submitted entity metadata.
- Tool and human input: tool cards, pending/settled filtering, approvals, and questions.
- Completion panel: prompt-to-text completion surface with custom output rendering.
- Tools end to end: server tool definition, streamed tool parts, and custom tool cards.
- Human review end to end: approval/question event contracts and decision routes.
- Attachments end to end: picker, dropzone, previews, and route handling.
- Persistence: initial messages, local state, server history, and reset behavior.
- Testing React UI: route contracts, fake streams, and component state tests.
Minimal message row
Use this when Thread.Messages has a child function and the app owns each row.
import { Message } from "@anvia/react-ui";
export function ChatMessage() {
return (
<Message.Root className="message">
<Message.Content className="message-content">
<Message.Parts />
</Message.Content>
<Message.Actions className="message-actions">
<Message.Copy>Copy</Message.Copy>
<Message.Regenerate>Try again</Message.Regenerate>
</Message.Actions>
</Message.Root>
);
}.message {
display: grid;
gap: 8px;
}
.message[data-role="user"] {
justify-items: end;
}
.message-content {
max-width: min(680px, 100%);
}
.message-actions {
display: flex;
gap: 6px;
}import { Message } from "@anvia/react-ui";
export function ChatMessage() {
return (
<Message.Root className="grid gap-2 data-[role=user]:justify-items-end">
<Message.Content className="max-w-[min(680px,100%)]">
<Message.Parts />
</Message.Content>
<Message.Actions className="flex gap-1.5 text-sm text-zinc-400">
<Message.Copy>Copy</Message.Copy>
<Message.Regenerate>Try again</Message.Regenerate>
</Message.Actions>
</Message.Root>
);
}Minimal composer
Use this as the base for a product composer. Add attachment controls and custom buttons as needed.
import { Composer } from "@anvia/react-ui";
export function ChatComposer() {
return (
<Composer.Root className="composer">
<Composer.Attachments className="attachments" />
<Composer.AddAttachment>Attach</Composer.AddAttachment>
<Composer.Input maxRows={6} placeholder="Send a message..." />
<Composer.Stop>Stop</Composer.Stop>
<Composer.Submit>Send</Composer.Submit>
</Composer.Root>
);
}.composer {
width: min(760px, calc(100% - 32px));
display: flex;
gap: 8px;
align-items: end;
}
.attachments {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.composer [data-anvia-composer-input] {
min-width: 0;
flex: 1;
}import { Composer } from "@anvia/react-ui";
export function ChatComposer() {
return (
<Composer.Root className="flex w-[min(760px,calc(100%-32px))] items-end gap-2">
<Composer.Attachments className="flex flex-wrap gap-2" />
<Composer.AddAttachment>Attach</Composer.AddAttachment>
<Composer.Input className="min-w-0 flex-1 resize-none" maxRows={6} placeholder="Send a message..." />
<Composer.Stop>Stop</Composer.Stop>
<Composer.Submit>Send</Composer.Submit>
</Composer.Root>
);
}