ChatComponent
ChatComponent is the all-in-one composition that provides a complete, styled chat interface. It combines message rendering, text input, typing indicator, error display, interrupt handling, and an optional thread sidebar into a single component.
ChatComponent ships with its own design tokens and component-scoped styles. No PostCSS config, no Tailwind setup, and no global stylesheet import is needed.
Selector: chat
Import:
#When to Use It
Use ChatComponent when you want a fully functional chat UI with minimal setup. It handles:
- Rendering human, AI, tool, and system messages with appropriate templates
- Markdown rendering for AI messages
- Auto-scrolling to new messages and streaming content
- Typing indicator while the agent is processing
- Error banner for agent errors
- Interrupt banner when the agent pauses
- Optional thread sidebar for multi-conversation support
- Enter to send, Shift+Enter for newlines
If you need to customize the message layout, add components between sections, or build a fundamentally different chat layout, use the primitives directly instead.
#Basic Usage
ChatComponent uses height: 100% with flex layout. Make sure its parent container has an explicit height, otherwise the component will collapse to zero height.
#API
#Inputs
| Input | Type | Default | Description |
|---|---|---|---|
agent | Agent | Required | The runtime-neutral agent providing streaming state. injectAgent() from @threadplane/langgraph returns a compatible LangGraphAgent. |
views | ViewRegistry | undefined | undefined | View registry for generative UI. Maps spec type names to Angular components. Created with views() from @threadplane/chat. |
store | StateStore | undefined | undefined | Optional state store for interactive generative UI specs. |
handlers | Record<string, (params: Record<string, unknown>) => unknown | Promise<unknown>> | {} | Event handlers for generative UI specs and consumer-owned A2UI local actions. Handlers run in Angular injection context — inject() is available inside handler functions. |
threads | Thread[] | [] | List of threads to display in the sidebar. Each thread must have an id property. |
activeThreadId | string | '' | The ID of the currently active thread, used for highlighting in the sidebar. |
welcomeDisabled | boolean | false | When true, suppresses the welcome screen shown for an empty conversation. |
modelOptions | readonly ChatSelectOption[] | [] | When non-empty, renders a <chat-select> model picker inside the input pill, wired to the two-way selectedModel. Leave empty to project your own <chat-select chatInputModelSelect>. |
showModelPicker | boolean | true | When false, hides the auto-rendered model picker even when modelOptions is non-empty. No effect on a consumer-projected <chat-select>. |
selectedModel | string (two-way) | '' | The selected model value, bound to the auto-rendered model picker. Use with [(selectedModel)]. |
modelPickerPlaceholder | string | 'Choose a model' | Placeholder shown in the auto-rendered model picker when no option matches. |
genuiToolNames | readonly string[] | ['generate_a2ui_schema', 'generate_json_render_spec', 'render_spec'] | Tool names whose calls produce a rendered GenUI surface rather than visible text. Used to filter <chat-tool-calls> and detect GenUI turns. |
#Outputs
| Output | Type | Description |
|---|---|---|
threadSelected | string | Emits when a thread is clicked in the sidebar. Payload is the thread ID. |
renderEvent | ChatRenderEvent | Emits render lifecycle events from generative UI surfaces. |
regenerate | void | Emits when the user clicks the regenerate button on an assistant message. |
rate | { messageIndex: number; rating: 'up' | 'down' } | Emits when the user rates an assistant message. |
messageCopy | { messageIndex: number; content: string } | Emits when the user copies an assistant message. |
#Thread Type
#Thread Sidebar
When you pass a non-empty threads array, a sidebar appears on the left (hidden on mobile by default):
#Message Feedback Outputs
Beyond threadSelected, <chat> emits three outputs for assistant-message feedback and regeneration. Each fires from the hover controls on an assistant message:
#Message Templates
ChatComponent ships with four built-in message templates:
| Type | Layout |
|---|---|
human | Right-aligned bubble with user background and border radius |
ai | Left-aligned with avatar badge, markdown-rendered content |
tool | Full-width monospace card with alt background |
system | Centered, italic, muted text |
#Generative UI
When you pass a [views] registry, the component auto-detects JSON specs in AI messages and renders them as Angular components:
AI messages containing JSON are parsed character-by-character as tokens stream. Components render incrementally — string props grow visibly as tokens arrive. See the Generative UI guide for full setup.
#Interactive specs and [store]
A StateStore is the shared reactive state that interactive generative-UI specs read from and write to. When a spec uses $state / $bindState prop expressions — for forms, selections, toggles — those bindings resolve against the store you pass via [store]. Read-only specs (a weather card, a chart) don't need one; reach for [store] only when a spec is interactive. Create it with signalStateStore() from @threadplane/render:
See State Store in the Generative UI guide for the full binding model.
#A2UI Rendering
When AI messages contain A2UI content (prefixed with ---a2ui_JSON---), the component auto-detects A2UI mode. Pass a2uiBasicCatalog() to [views] to render the built-in catalog:
The catalog currently maps 18 component types: Text, Image, Icon, Divider, Row, Column, Card, List, Button, TextField, CheckBox, MultipleChoice, DateTimeInput, Slider, Tabs, Modal, Video, and AudioPlayer.
A2UI surfaces support two-way data binding, button actions, template expansion over collections, and validation. See the A2UI guide for details.
#Auto-Scroll Behavior
The component tracks message count and loading state to auto-scroll:
- New message: Scrolls to bottom immediately (using
behavior: 'instant') - Streaming updates: Scrolls smoothly only if the user is near the bottom (within 150px)
- User scrolled up: Does not auto-scroll during streaming to avoid disrupting reading
#Message Pattern
ChatComponent renders messages using an asymmetric pattern:
- User messages — right-aligned filled bubble using
--tplane-chat-primarybackground and--tplane-chat-on-primarytext - Assistant messages — inline (no bubble), left-aligned, with hover controls (copy, thumbs up/down) that appear on pointer-over
#Styling
Override any CSS custom property on the chat element or a parent. See the Theming guide for the full --tplane-chat-* token reference.
#Included Primitives
Under the hood, ChatComponent composes these primitives:
ChatMessageListComponentwithMessageTemplateDirectivefor message renderingChatInputComponentfor text input (withsubmitOnEnterand a placeholder)ChatTypingIndicatorComponentfor the animated typing dotsChatErrorComponentfor error displayChatInterruptComponentfor the interrupt bannerChatThreadListComponentfor the sidebar
#Reasoning
When a model emits reasoning content (gpt-5 / o-series with reasoning blocks, Anthropic with thinking blocks, or any AG-UI agent emitting REASONING_MESSAGE_* events), the adapter populates Message.reasoning and Message.reasoningDurationMs. The <chat> composition automatically renders <chat-reasoning> above the assistant response. No configuration required.
While reasoning is streaming, the pill shows "Thinking…" with a pulse dot and the body auto-expands so the user sees content arrive in real time. Once response text begins, the pill collapses to "Thought for Ns" (e.g. "Thought for 4s").
#Tool-call templates
Project a <ng-template chatToolCallTemplate="…"> directly into <chat> to replace the default card UX for a specific tool name. The composition forwards the template into the inner <chat-tool-calls>.
A chatToolCallTemplate="*" wildcard catches any unmapped tool name. See chatToolCallTemplate for the directive reference.