Overview
CopilotKit provides pre-built Angular components for chat interfaces. These components are standalone, support signals for reactivity, and offer extensive customization through slots and templates.
CopilotChatMessageView
Renders a list of messages with support for assistant and user messages, custom layouts, and tool call rendering.
Usage
import { Component } from '@angular/core';
import { CopilotChatMessageView } from '@copilotkit/angular';
@Component({
selector: 'app-chat',
standalone: true,
imports: [CopilotChatMessageView],
template: `
<copilot-chat-message-view
[messages]="messages"
[isLoading]="isLoading"
[showCursor]="showCursor"
/>
`
})
export class ChatComponent {
messages = signal<Message[]>([]);
isLoading = signal(false);
showCursor = signal(true);
}
Array of messages to display. Messages should have a role of 'user', 'assistant', or 'tool'.
Whether to show a blinking cursor at the end of the message list (typically while loading).
Whether the agent is currently processing a response.
CSS classes to apply to the container element.
assistantMessageComponent
Custom component to render assistant messages.
Custom template to render assistant messages.
CSS classes to apply to assistant messages.
Custom component to render user messages.
Custom template to render user messages.
CSS classes to apply to user messages.
Custom component for the cursor.
Custom template for the cursor.
CSS classes to apply to the cursor.
Outputs
assistantMessageThumbsUp
EventEmitter<{ message: Message }>
Emitted when user clicks thumbs up on an assistant message.
assistantMessageThumbsDown
EventEmitter<{ message: Message }>
Emitted when user clicks thumbs down on an assistant message.
assistantMessageReadAloud
EventEmitter<{ message: Message }>
Emitted when user clicks read aloud on an assistant message.
assistantMessageRegenerate
EventEmitter<{ message: Message }>
Emitted when user requests message regeneration.
userMessageCopy
EventEmitter<{ message: Message }>
Emitted when user copies a user message.
userMessageEdit
EventEmitter<{ message: Message }>
Emitted when user edits a user message.
Examples
Basic Message View
import { Component, signal } from '@angular/core';
import { CopilotChatMessageView } from '@copilotkit/angular';
import type { Message } from '@ag-ui/core';
@Component({
selector: 'app-chat',
standalone: true,
imports: [CopilotChatMessageView],
template: `
<copilot-chat-message-view
[messages]="messages()"
[isLoading]="isLoading()"
[showCursor]="isLoading()"
(assistantMessageThumbsUp)="handleThumbsUp($event)"
(assistantMessageThumbsDown)="handleThumbsDown($event)"
/>
`
})
export class ChatComponent {
messages = signal<Message[]>([
{ id: '1', role: 'user', content: 'Hello!' },
{ id: '2', role: 'assistant', content: 'Hi! How can I help you?' }
]);
isLoading = signal(false);
handleThumbsUp(event: { message: Message }) {
console.log('Thumbs up:', event.message);
}
handleThumbsDown(event: { message: Message }) {
console.log('Thumbs down:', event.message);
}
}
Custom Layout Template
@Component({
selector: 'app-chat',
standalone: true,
imports: [CopilotChatMessageView],
template: `
<copilot-chat-message-view [messages]="messages()">
<ng-template #customLayout let-messages="messages" let-isLoading="isLoading">
<div class="custom-layout">
<h2>Chat Messages</h2>
@for (message of messages; track message.id) {
<div class="message-{{ message.role }}">
{{ message.content }}
</div>
}
@if (isLoading) {
<div class="loading">Loading...</div>
}
</div>
</ng-template>
</copilot-chat-message-view>
`
})
export class ChatComponent {
messages = signal<Message[]>([]);
}
Custom Message Components
// custom-assistant-message.component.ts
@Component({
selector: 'app-custom-assistant-message',
standalone: true,
template: `
<div class="custom-assistant">
<strong>AI:</strong> {{ message.content }}
</div>
`
})
export class CustomAssistantMessageComponent {
@Input() message!: Message;
}
// chat.component.ts
@Component({
selector: 'app-chat',
standalone: true,
imports: [CopilotChatMessageView],
template: `
<copilot-chat-message-view
[messages]="messages()"
[assistantMessageComponent]="customAssistantComponent"
/>
`
})
export class ChatComponent {
messages = signal<Message[]>([]);
customAssistantComponent = CustomAssistantMessageComponent;
}
An input component for sending messages, with support for voice transcription, file uploads, and tool menus.
Usage
import { Component, signal } from '@angular/core';
import { CopilotChatInput } from '@copilotkit/angular';
@Component({
selector: 'app-chat',
standalone: true,
imports: [CopilotChatInput],
template: `
<copilot-chat-input
[value]="inputValue()"
(submitMessage)="handleSubmit($event)"
(valueChange)="inputValue.set($event)"
/>
`
})
export class ChatComponent {
inputValue = signal('');
handleSubmit(message: string) {
console.log('Submitted:', message);
this.inputValue.set('');
}
}
Current value of the input field.
mode
'input' | 'transcribe' | 'processing'
Current input mode. Controls the UI state.
Whether to auto-focus the input on mount.
CSS classes to apply to the wrapper element.
Array of tool menu items to display. Use '-' for separators.
Template for additional toolbar items.
CSS classes for the send button.
Custom component for the send button.
CSS classes for the textarea.
Custom component for the textarea.
Maximum number of rows for the auto-expanding textarea.
Placeholder text for the textarea.
CSS classes for the toolbar.
Custom component for the toolbar.
CSS classes for the audio recorder.
Custom component for the audio recorder.
Outputs
Emitted when user submits a message (Enter key or send button).
Emitted when the input value changes.
Emitted when voice transcription starts.
Emitted when voice transcription is cancelled.
Emitted when voice transcription finishes.
Emitted when the add file button is clicked.
Examples
import { Component, signal } from '@angular/core';
import { CopilotChatInput } from '@copilotkit/angular';
@Component({
selector: 'app-chat-input',
standalone: true,
imports: [CopilotChatInput],
template: `
<copilot-chat-input
[value]="value()"
[textAreaPlaceholder]="'Ask me anything...'"
(submitMessage)="onSubmit($event)"
(valueChange)="value.set($event)"
/>
`
})
export class ChatInputComponent {
value = signal('');
onSubmit(message: string) {
console.log('Message:', message);
// Send to agent
}
}
With Voice Transcription
@Component({
selector: 'app-chat-input',
standalone: true,
imports: [CopilotChatInput],
template: `
<copilot-chat-input
[value]="value()"
[mode]="mode()"
(submitMessage)="onSubmit($event)"
(startTranscribe)="mode.set('transcribe')"
(cancelTranscribe)="mode.set('input')"
(finishTranscribe)="onFinishTranscribe()"
/>
`
})
export class ChatInputComponent {
value = signal('');
mode = signal<'input' | 'transcribe' | 'processing'>('input');
onSubmit(message: string) {
this.mode.set('processing');
// Send to agent
}
onFinishTranscribe() {
this.mode.set('input');
// Transcribed text is automatically set to value
}
}
import type { ToolsMenuItem } from '@copilotkit/angular';
@Component({
selector: 'app-chat-input',
standalone: true,
imports: [CopilotChatInput],
template: `
<copilot-chat-input
[toolsMenu]="toolsMenu"
(submitMessage)="onSubmit($event)"
/>
`
})
export class ChatInputComponent {
toolsMenu: ToolsMenuItem[] = [
{
name: 'Search Database',
icon: '🔍',
onClick: () => this.triggerSearch()
},
'-', // Separator
{
name: 'Export Data',
icon: '📥',
onClick: () => this.exportData()
}
];
onSubmit(message: string) {
// Handle message
}
triggerSearch() {
console.log('Triggering search...');
}
exportData() {
console.log('Exporting data...');
}
}
// custom-send-button.component.ts
@Component({
selector: 'app-custom-send-button',
standalone: true,
template: `
<button
[disabled]="disabled"
(click)="send()"
class="custom-send-btn"
>
Send Message
</button>
`
})
export class CustomSendButtonComponent {
@Input() send!: () => void;
@Input() disabled!: boolean;
@Input() value!: string;
}
// chat.component.ts
@Component({
selector: 'app-chat',
standalone: true,
imports: [CopilotChatInput],
template: `
<copilot-chat-input
[sendButtonComponent]="customSendButton"
(submitMessage)="onSubmit($event)"
/>
`
})
export class ChatComponent {
customSendButton = CustomSendButtonComponent;
onSubmit(message: string) {
console.log('Submitted:', message);
}
}
@Component({
selector: 'app-chat-input',
standalone: true,
imports: [CopilotChatInput],
template: `
<copilot-chat-input (submitMessage)="onSubmit($event)">
<ng-template #additionalItems>
<button (click)="clearHistory()">Clear</button>
<button (click)="exportChat()">Export</button>
</ng-template>
</copilot-chat-input>
`
})
export class ChatInputComponent {
onSubmit(message: string) {
// Handle submit
}
clearHistory() {
console.log('Clearing history...');
}
exportChat() {
console.log('Exporting chat...');
}
}
Styling
All components support Tailwind CSS classes and can be fully customized:
<copilot-chat-message-view
[inputClass]="'bg-gray-100 p-4 rounded-lg'"
[assistantMessageClass]="'bg-blue-50 border-blue-200'"
[userMessageClass]="'bg-green-50 border-green-200'"
/>
<copilot-chat-input
[inputClass]="'shadow-xl'"
[sendButtonClass]="'bg-primary text-white'"
[textAreaClass]="'min-h-[100px]'"
/>
See Also