Docs

React UI

Full chat surface

Compose a complete @anvia/react-ui chat surface with messages, suggestions, scrolling, and composer controls.

This recipe starts from a useChat(...) controller and builds a complete app-owned chat screen. It intentionally uses the default message renderer first. Add a custom message row only after you need custom Markdown, tool cards, attachments, or actions.

The recipe assumes a compatible API app endpoint. If you do not have one yet, build the app from TanStack quickstart or wire the endpoint with Server routes.

import { useChat } from "@anvia/react";
import { ChatProvider, Composer, Thread } from "@anvia/react-ui";
import "@anvia/react-ui/styles.css";

export function AgentChat() {
  const chat = useChat({
    endpoint: "http://localhost:8787/api/chat",
    suggestions: [
      { id: "summarize", label: "Summarize", prompt: "Summarize this conversation." },
      { id: "risks", label: "Find risks", prompt: "What are the risks in this plan?" },
      { id: "next", label: "Next step", prompt: "Suggest the next implementation step." },
    ],
  });

  return (
    <ChatProvider controller={chat}>
      <Thread.Root className="chat">
        <Thread.Viewport className="chat-scroll">
          <Thread.Empty className="empty-state">
            <h1>What should we build?</h1>
            <Thread.Suggestions className="suggestions" />
          </Thread.Empty>

          <Thread.Messages className="messages" />
          <Thread.Error className="thread-error" />
          <Thread.ScrollToBottom className="scroll-button">Jump to latest</Thread.ScrollToBottom>
        </Thread.Viewport>

        <Composer.Root className="composer">
          <Composer.Attachments className="composer-attachments" />
          <Composer.AddAttachment>Attach</Composer.AddAttachment>
          <Composer.Input minRows={1} maxRows={6} placeholder="Message the agent..." />
          <Composer.Stop>Stop</Composer.Stop>
          <Composer.Submit>Send</Composer.Submit>
        </Composer.Root>
      </Thread.Root>
    </ChatProvider>
  );
}

Add message actions

If you need copy or regenerate controls, switch Thread.Messages to a child function and keep Message.Parts as the renderer.

<Thread.Messages className="messages">
  {(message) => (
    <Message.Root className="message">
      <Message.Content className="message-content">
        <Message.Parts />
      </Message.Content>
      {message.role === "assistant" ? (
        <Message.Actions className="message-actions">
          <Message.Copy>Copy</Message.Copy>
          <Message.Regenerate>Regenerate</Message.Regenerate>
        </Message.Actions>
      ) : null}
    </Message.Root>
  )}
</Thread.Messages>

For deeper message customization, continue to Message rendering.