Chat is the side panel for in-room messages. Auto-scroll to bottom on new messages unless the user has scrolled up (then a "N new ↓" pill appears). Bodies render as plain text by default — no XSS surface.
Install
pnpm add @levelchat/react-components @levelchat/webBasic usage
import { Chat } from '@levelchat/react-components';
<Chat className="h-[480px] w-[320px]" />Headless hook
import { useChat } from '@levelchat/react-components';
function MyChat() {
const { messages, send, supported } = useChat();
if (!supported) return null;
return (
<form onSubmit={(e) => { e.preventDefault(); send(input.value); }}>
<ul>{messages.map((m) => <li key={m.id}>{m.body}</li>)}</ul>
<input name="input" />
</form>
);
}Custom message rendering
Inject renderBody to add link auto-detection, mention pills, emoji shortcodes, etc. Keep it pure — never use dangerouslySetInnerHTML.
<Chat renderBody={(body) => <Linkify>{body}</Linkify>} />Security
- Bodies render via React text nodes — no
dangerouslySetInnerHTML. - SDK enforces an 8KB cap; longer pastes are truncated upstream.
- Links are NOT auto-linkified by this component — that's a consumer concern (your custom
renderBody), so a phishing URL isn't clickable for free.
Accessibility
role="log"+aria-live="polite"on the message stream.- Composer's send button has
aria-label; Enter sends, Shift+Enter inserts a newline. - Timestamps render with locale-aware
Intl.DateTimeFormat.
Props
| Prop | Type | Default | Notes |
|---|---|---|---|
readOnly | boolean | false | Hide the composer (audience view). |
placeholder | string | Send a message… | Composer placeholder text. |
renderMessage | (m: ChatMessage) => ReactNode | — | Replace the whole message bubble. |
renderBody | (body: string) => ReactNode | — | Replace just the message body content. |
hideHeader | boolean | false | Hide the panel header strip. |