Docs

React UI

Mental model

Understand how @anvia/react controllers, React UI providers, and compound component namespaces fit together.

React UI is easiest to read as a set of context-bound building blocks, not as a closed chat widget. The dotted names such as Thread.Messages, Composer.Input, and Message.Parts are compound components. Each namespace groups primitives that share the same controller state and local context.

The shape

@anvia/react owns transport and state reduction. @anvia/react-ui renders that state through providers and primitives. Your app owns the server route, styling, auth, persistence, and any domain-specific rendering.

There are two surface families:

  • Chat: useChat(...) -> ChatProvider -> Thread.*, Composer.*, Message.*, and HumanInput.*.
  • Completion: useCompletion(...) -> CompletionProvider -> Completion.*.

They can share the same wire request shape, but they do not share UI context. Use chat for transcripts, tools, attachments, and human review. Use completion for one prompt and one generated text result.

Browser client
useChat(...) or useCompletion(...) creates a controller.
ChatProvider or CompletionProvider exposes it to UI primitives.
Thread.*, Composer.*, and Message.* read chat context; Completion.* reads completion context.
UI actions
Submit, stop, regenerate, approve, reject, and answer controls call controller actions.
POST / stream
Application API route
Reads { messages, stream: true } and request metadata.
Runs auth, model selection, agents, tools, and persistence.
Returns Anvia stream events to the controller.
Stream reduction
The hook folds stream events into messages, status, errors, suggestions, and human input.

Data flows down from the hook controller into providers and primitives. User actions flow back up through controller methods such as sendMessage, stop, regenerate, approval handlers, or question handlers.

How to read dotted components

The first word is the context family. The second word is the primitive rendered inside that family.

Pattern Meaning
ChatProvider Makes a useChat(...) controller available to chat primitives.
Thread.Root Creates thread context around the transcript area and emits thread state.
Thread.Messages Iterates chat.messages and installs message context for each row.
Message.Root Reads the current message and emits message attributes such as data-role.
Message.Parts Iterates message.parts and installs part context for each part.
Composer.Root Creates composer context for draft text, pending attachments, submit, and stop.
Composer.Input Reads and writes the nearest composer draft.
HumanInput.Panel Reads pending approvals and questions from the chat controller.

The same pattern applies to CompletionProvider and Completion.*, except completion surfaces use a useCompletion(...) controller instead of a chat controller.

CompletionProvider does not provide chat contexts. If a surface needs Thread, Composer, Message, tools, attachments, or human input, use useChat(...) and ChatProvider.

The common chat tree

Start with the smallest tree that lets the package render messages for you:

const chat = useChat({ endpoint: "http://localhost:8787/api/chat" });

return (
  <ChatProvider controller={chat}>
    <Thread.Root>
      <Thread.Viewport autoScroll>
        <Thread.Empty>Ask your first question.</Thread.Empty>
        <Thread.Messages />
        <Thread.Error />
        <Thread.ScrollToBottom>Latest</Thread.ScrollToBottom>
      </Thread.Viewport>

      <Composer.Root>
        <Composer.Attachments />
        <Composer.Input placeholder="Message Anvia..." />
        <Composer.Stop>Stop</Composer.Stop>
        <Composer.Submit>Send</Composer.Submit>
      </Composer.Root>
    </Thread.Root>
  </ChatProvider>
);

Thread.Messages has no child function here, so it uses the default row and part renderer. This is the preferred starting point because new message part types can render without application changes.

Rendering levels

Move down one level only when the product needs control at that level.

Level You keep You customize
Default list Thread.Messages CSS through stable data-anvia-* and data-role attributes.
Message row Message.Parts Row layout, avatars, actions, timestamps, copy, regenerate.
Message part Message.Part fallback Tool cards, Markdown, attachment previews, structured data.
Route request UI primitives Model selectors, metadata, auth, persistence, and tool execution.

For example, customize the row but keep default part rendering:

<Thread.Messages>
  {(message) => (
    <Message.Root className="message-row">
      <Message.Content>
        <Message.Parts />
      </Message.Content>
      <Message.Actions>
        <Message.Copy>Copy</Message.Copy>
        <Message.Regenerate>Try again</Message.Regenerate>
      </Message.Actions>
    </Message.Root>
  )}
</Thread.Messages>

Customize individual parts only when one part type needs product-specific UI. Keep the fallback:

<Message.Parts>
  {(part) => {
    if (part.type === "tool") {
      return (
        <Message.Part>
          <Message.Tool className="tool-card" renderWhen="always" />
        </Message.Part>
      );
    }

    return <Message.Part />;
  }}
</Message.Parts>

Boundaries to remember

  • The browser renders UI and calls your API route. It should not hold provider credentials or run provider SDKs directly.
  • @anvia/react-ui does not create a controller. It expects a useChat(...) or useCompletion(...) result from @anvia/react.
  • Use createRequest on useChat(...) for request-level options such as model, tenant, or reasoning effort. Use Composer.Root submitMessage only when the submitted message itself needs custom text, attachments, or metadata.
  • Prefer the default Thread.Messages and Message.Part fallbacks until a product-specific part needs custom markup.
  • Style stable attributes such as data-anvia-message, data-role, data-part, and data-state instead of relying on internal DOM details.

Next steps

  • Cheat sheet: grab imports, skeletons, renderer rules, and common snippets.
  • Getting started: render the first chat surface.
  • Request shape: add model selectors and metadata.
  • Messages: choose the right renderer level.
  • Styling: target stable attributes from application CSS.