UI Components

UI Components

@react-ai-stream/ui ships a set of unstyled-but-styled components you can use directly or treat as a reference for your own design system.

npm install @react-ai-stream/ui

Then import the base styles once at your app root:

import '@react-ai-stream/ui/styles'

<Chat>

The top-level component. Combines MessageList and ChatInput in a single wrapper.

import { Chat } from '@react-ai-stream/ui'
 
<Chat
  messages={messages}
  onSend={sendMessage}
  onStop={stop}
  loading={loading}
  placeholder="Type a message…"
  className="my-chat"
/>

Props

PropTypeRequiredDescription
messagesMessage[]yesFull conversation history from useAIChat
onSend(text: string) => voidyesCalled when the user submits a message
onStop() => voidnoCalled when the Stop button is clicked
loadingbooleannoShows typing indicator and Stop button while true
placeholderstringnoTextarea placeholder. Default: "Type a message…"
classNamestringnoExtra CSS class on the outer wrapper

Typing indicator

The typing indicator appears when loading is true and the last message has no content yet (i.e., streaming just started). Once tokens arrive, the indicator hides and the assistant bubble fills in.


<MessageList>

Renders the conversation history and auto-scrolls to the bottom on new content.

import { MessageList } from '@react-ai-stream/ui'
 
<MessageList messages={messages} loading={loading} />

Props

PropTypeRequiredDescription
messagesMessage[]yesConversation history. System messages are filtered out automatically
loadingbooleannoWhen true, shows the three-dot typing indicator at the bottom

<MessageBubble>

Renders a single message. User messages are plain text; assistant messages go through MarkdownRenderer.

import { MessageBubble } from '@react-ai-stream/ui'
 
<MessageBubble message={message} />

Props

PropTypeRequiredDescription
messageMessageyesA single message object from the messages array

CSS classes

ClassApplied to
ras-messageOuter wrapper
ras-message--userAdded for user messages
ras-message--assistantAdded for assistant messages
ras-message__bubbleThe bubble container
ras-message__textUser message text

<ChatInput>

The input area with an auto-send button and optional Stop button.

import { ChatInput } from '@react-ai-stream/ui'
 
<ChatInput
  onSend={sendMessage}
  onStop={stop}
  loading={loading}
  placeholder="Ask something…"
  disabled={false}
/>

Props

PropTypeRequiredDescription
onSend(text: string) => voidyesCalled with trimmed text on submit
onStop() => voidnoIf provided, replaces Send with a Stop button while loading is true
loadingbooleannoDisables the textarea and swaps Send → Stop
placeholderstringnoTextarea placeholder
disabledbooleannoHard-disables the input regardless of loading state

Keyboard: Enter sends, Shift+Enter inserts a newline.


<MarkdownRenderer>

Renders markdown with GitHub-flavored extensions and syntax-highlighted code blocks. Code blocks include a copy button.

import { MarkdownRenderer } from '@react-ai-stream/ui'
 
<MarkdownRenderer content={markdownString} className="my-prose" />

Props

PropTypeRequiredDescription
contentstringyesMarkdown string to render
classNamestringnoExtra CSS class on the wrapper div

Features

  • GitHub Flavored Markdown (tables, strikethrough, task lists)
  • Syntax highlighting via rehype-highlight (uses highlight.js themes)
  • Copy button on fenced code blocks — copies the raw text, not highlighted spans

Bring your own highlight theme

rehype-highlight uses highlight.js CSS. Import any highlight.js theme alongside @react-ai-stream/ui/styles:

import '@react-ai-stream/ui/styles'
import 'highlight.js/styles/github-dark.css'   // or any other theme

useCopyToClipboard

A small hook that tracks copied state with auto-reset.

import { useCopyToClipboard } from '@react-ai-stream/ui'
 
function CopyButton({ text }: { text: string }) {
  const { copied, copy } = useCopyToClipboard(2000)
  return (
    <button onClick={() => copy(text)}>
      {copied ? 'Copied!' : 'Copy'}
    </button>
  )
}

Arguments

ArgTypeDefaultDescription
resetMsnumber2000Milliseconds before copied resets to false

Returns

ValueTypeDescription
copiedbooleantrue for resetMs ms after a successful copy
copy(text: string) => Promise<void>Writes text to clipboard; no-op if clipboard API is unavailable

CSS custom properties

All component styles use the .ras-* namespace. You can override them in your own CSS after importing the base sheet:

/* override chat container */
.ras-chat { border-radius: 0; }
 
/* override user bubble color */
.ras-message--user .ras-message__bubble { background: #7c3aed; }
 
/* override assistant bubble */
.ras-message--assistant .ras-message__bubble { background: #1e1e2e; color: #cdd6f4; }
💡

For full design-system control, skip @react-ai-stream/ui entirely and wire useAIChat to your own components. See the custom UI example.