examples/v2/docs/reference/copilot-chat-message-view.mdx
CopilotChatMessageView is the default component used by CopilotChat to render the message list. It handles rendering user messages, assistant messages, activity messages, and custom message renderers with optimized memoization for performance.
The CopilotChatMessageView component:
CopilotChatMessageView provides slots for customizing message rendering:
graph LR
MV[CopilotChatMessageView] --> assistantMessage
MV --> userMessage
MV --> cursor
| Slot | Description |
|---|---|
assistantMessage | Component for rendering assistant (AI) messages |
userMessage | Component for rendering user messages |
cursor | Typing indicator shown during streaming |
Customize the message view through the messageView prop on CopilotChat:
<CopilotChat
messageView={{
className: "space-y-4 p-4",
assistantMessage: "bg-blue-50 rounded-lg",
userMessage: "bg-gray-100 rounded-lg",
}}
/>
CopilotChatMessageView uses the slot system. Each slot accepts four types of values:
The assistantMessage slot controls how AI responses are rendered:
<CopilotChat
messageView={{
assistantMessage: {
className: "bg-slate-50 border border-slate-200 rounded-xl p-4",
onThumbsUp: (message) => sendFeedback(message.id, "positive"),
onThumbsDown: (message) => sendFeedback(message.id, "negative"),
onRegenerate: (message) => regenerateResponse(message.id),
},
}}
/>
| Sub-Slot | Description |
|---|---|
markdownRenderer | Renders the message content with markdown support |
toolbar | Container for action buttons |
copyButton | Button to copy message content |
thumbsUpButton | Positive feedback button |
thumbsDownButton | Negative feedback button |
readAloudButton | Text-to-speech button |
regenerateButton | Button to regenerate the response |
toolCallsView | Renders tool call executions |
For full details, see CopilotChatAssistantMessage.
The userMessage slot controls how user messages are rendered:
<CopilotChat
messageView={{
userMessage: {
className: "bg-primary text-primary-foreground rounded-2xl px-4 py-2",
onEditMessage: ({ message }) => editMessage(message),
},
}}
/>
| Sub-Slot | Description |
|---|---|
messageRenderer | Renders the message text content |
toolbar | Container for action buttons |
copyButton | Button to copy message content |
editButton | Button to edit the message |
branchNavigation | Navigation for conversation branches |
For full details, see CopilotChatUserMessage.
The cursor appears while the AI is generating a response:
<CopilotChat
messageView={{
cursor: "bg-blue-500 w-3 h-3",
}}
/>
Or with a custom component:
function CustomCursor() {
return (
<div className="flex gap-1">
<span className="w-2 h-2 bg-blue-500 rounded-full animate-bounce" />
<span className="w-2 h-2 bg-blue-500 rounded-full animate-bounce delay-100" />
<span className="w-2 h-2 bg-blue-500 rounded-full animate-bounce delay-200" />
</div>
);
}
<CopilotChat
messageView={{
cursor: CustomCursor,
}}
/>;
To completely replace the message view with your own component, pass a custom component to the messageView prop:
import { CopilotChatMessageView } from "@copilotkit/react-core";
function CustomMessageView({ messages, isRunning, ...props }) {
return (
<div className="custom-message-list">
<div className="message-count text-sm text-muted-foreground mb-4">
{messages.length} messages
</div>
<CopilotChatMessageView
messages={messages}
isRunning={isRunning}
className="space-y-6"
assistantMessage="bg-white shadow-sm rounded-xl p-4"
userMessage="bg-blue-500 text-white rounded-2xl"
{...props}
/>
{isRunning && (
<div className="text-center text-muted-foreground mt-4">
AI is thinking...
</div>
)}
</div>
);
}
<CopilotChat messageView={CustomMessageView} />;
For even more control, use the children render function pattern:
function CustomMessageView({ messages, isRunning }) {
return (
<CopilotChatMessageView messages={messages} isRunning={isRunning}>
{({ messageElements, messages, isRunning }) => (
<div className="custom-layout">
<header className="sticky top-0 bg-background p-2 border-b">
{messages.length} messages
</header>
<div className="messages p-4">{messageElements}</div>
{isRunning && (
<footer className="sticky bottom-0 bg-background p-2">
Generating response...
</footer>
)}
</div>
)}
</CopilotChatMessageView>
);
}
<CopilotChat messageView={CustomMessageView} />;
The render function receives:
| Property | Type | Description |
|---|---|---|
messageElements | ReactElement[] | Pre-rendered message components |
messages | Message[] | Raw message data |
isRunning | boolean | Whether AI is generating |
<CopilotChat
messageView={{
className: "space-y-6 p-4",
assistantMessage: "bg-white shadow-sm rounded-xl p-4",
userMessage: "bg-blue-500 text-white rounded-2xl px-4 py-2",
}}
/>
<CopilotChat
messageView={{
assistantMessage: {
onThumbsUp: (message) => {
analytics.track("positive_feedback", { messageId: message.id });
},
onThumbsDown: (message) => {
analytics.track("negative_feedback", { messageId: message.id });
},
},
}}
/>
Replace the assistant message component entirely:
function CustomAssistantMessage({ message, isRunning }) {
return (
<div className="flex gap-3">
<Avatar src="/bot-avatar.png" />
<div className="flex-1">
<Markdown>{message.content}</Markdown>
{isRunning && <TypingIndicator />}
</div>
</div>
);
}
<CopilotChat
messageView={{
assistantMessage: CustomAssistantMessage,
}}
/>;