LevelChatLevelChatDocs
Chat

Components

Chat

Side panel for in-room messaging.

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/web

Requirements

  • Reactreact and react-dom ^18 || ^19 (peer dependency).
  • LevelChat Web SDK@levelchat/web ^0.2.4 (peer dependency, wires hook-driven mode to the live Room).
  • Provider — wrap the tree in <LevelChatProvider> from @levelchat/web-react so hook-driven mode can subscribe to room events. Prop-driven demos work without it.
  • Styles — import the brand tokens + kit stylesheet exactly once at the app entry, before any kit component renders.

In Next.js (App Router), import the stylesheets at the top of your root layout:

TypeScript (TSX)
// app/layout.tsx
import '@levelchat/brand/tokens.css';
import '@levelchat/react-components/styles.css';

In Vite (or any plain React app), import them at the top of your entry module:

TypeScript (TSX)
// src/main.tsx
import '@levelchat/brand/tokens.css';
import '@levelchat/react-components/styles.css';

Basic usage

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

PropTypeDefaultNotes
readOnlybooleanfalseHide the composer (audience view).
placeholderstringSend a message…Composer placeholder text.
renderMessage(m: ChatMessage) => ReactNodeReplace the whole message bubble.
renderBody(body: string) => ReactNodeReplace just the message body content.
hideHeaderbooleanfalseHide the panel header strip.