Docs

React UI

Styling

Style headless Anvia React UI primitives with class names, data attributes, Vanilla CSS, or Tailwind CSS.

@anvia/react-ui is headless. The package owns behavior, state, context, and stable attributes. Your application owns layout, spacing, color, typography, and design-system integration.

What the package styles

The optional stylesheet is for prototypes and functional defaults:

import "@anvia/react-ui/styles.css";

It keeps rich composer and textarea resizing, disabled states, media bounds, code wrapping, and basic overflow sane. It does not try to create your final chat layout.

What your app styles

Your app should style:

  • Page and viewport height.
  • Scroll container padding.
  • Message list width and gaps.
  • User and assistant message alignment.
  • Bubbles, cards, tool-call panels, and attachment previews.
  • Composer width, controls, and action states.

Chat shell

Start by making the thread and composer behave like one surface.

import { Composer, Thread } from "@anvia/react-ui";

export function ChatShell() {
  return (
    <Thread.Root className="chat">
      <Thread.Viewport className="chat-scroll">
        <Thread.Empty className="empty-state">Start a conversation.</Thread.Empty>
        <Thread.Messages className="messages" />
        <Thread.Error className="thread-error" />
        <Thread.ScrollToBottom className="scroll-button">Latest</Thread.ScrollToBottom>
      </Thread.Viewport>

      <Composer.Root className="composer">
        <Composer.Attachments className="composer-attachments" />
        <Composer.AddAttachment>Attach</Composer.AddAttachment>
        <Composer.Input maxRows={6} placeholder="Ask a question..." />
        <Composer.Stop>Stop</Composer.Stop>
        <Composer.Submit>Send</Composer.Submit>
      </Composer.Root>
    </Thread.Root>
  );
}

Message rows

Use data-role on Message.Root for role-specific alignment and bubbles. This works for both the default part renderer and custom part renderers.

import { Message, Thread } from "@anvia/react-ui";

export function Messages() {
  return (
    <Thread.Messages className="messages">
      {(message) => (
        <Message.Root className="message">
          <Message.Content className="message-content">
            <Message.Parts />
          </Message.Content>
        </Message.Root>
      )}
    </Thread.Messages>
  );
}

Composer

Composer primitives emit state attributes, so the submit and stop controls can change based on whether the chat is idle, submitting, or streaming.

import { Composer } from "@anvia/react-ui";

export function ComposerBar() {
  return (
    <Composer.Root className="composer">
      <Composer.Attachments className="composer-attachments" />
      <Composer.AddAttachment className="composer-button">Attach</Composer.AddAttachment>
      <Composer.Input className="composer-input" maxRows={6} />
      <Composer.Stop className="composer-button">Stop</Composer.Stop>
      <Composer.Submit className="composer-button composer-submit">Send</Composer.Submit>
    </Composer.Root>
  );
}

Data attributes

Use attributes when styling should follow primitive state instead of a component name.

[data-anvia-message][data-role="user"] {
  justify-items: end;
}

[data-anvia-tool][data-state="output-available"] {
  border-color: #22c55e;
}

[data-anvia-composer][data-state="streaming"] [data-anvia-submit] {
  display: none;
}

[data-anvia-scroll-to-bottom][data-state="bottom"] {
  visibility: hidden;
}

Common attributes:

Attribute Emitted by Use
data-anvia-thread Thread.Root Root chat state.
data-anvia-thread-viewport Thread.Viewport Scroll container state.
data-anvia-message Message.Root Message row styling.
data-role Message.Root user, assistant, system, or tool alignment.
data-anvia-part Message.Part Per-part layout.
data-anvia-tool Message.Tool Tool-call cards.
data-anvia-composer Composer.Root Composer state.
data-anvia-composer-input Composer.Input and Composer.TextareaInput Prompt input wrapper.
data-anvia-composer-editor Rich editor inside Composer.Input Contenteditable editor styling.
data-anvia-composer-textarea-input Composer.TextareaInput Native textarea fallback styling.
data-anvia-composer-entity Inline trigger entity Mention or command chip styling.
data-anvia-composer-trigger-menu Composer.TriggerMenu Floating trigger menu.
data-anvia-composer-trigger-item Composer.TriggerItem Trigger menu option styling.
data-anvia-submit Composer.Submit Submit enabled/disabled state.
data-anvia-human-input-panel HumanInput.Panel Review panel layout.

When using a design system, attach primitive behavior to your component with asChild:

<Composer.Submit asChild>
  <Button type="submit" variant="primary">
    Send
  </Button>
</Composer.Submit>

For a complete copy-paste surface, see Styling recipe.