CopilotChatInput
Primary text input and control surface for chat interactions in Vue 3
Overview
CopilotChatInput is the Vue 3 primary text input and control surface for chat interactions. It provides a multi-line textarea that auto-grows up to a configurable maximum number of rows (default 5), action buttons for sending messages and voice transcription, and an optional tools menu for declarative command surfaces (which also surfaces a file-attachment item when you bind @add-file, and is reachable via inline / slash commands).
The component operates in three modes: "input" (default text entry), "transcribe" (replaces the textarea with an audio recorder), and "processing" (shows a spinner while background processing is underway). When text spans multiple rows, the layout stacks the textarea above the control row.
It supports both v-model (via modelValue / update:modelValue) and uncontrolled usage. When uncontrolled, the component manages its own text state internally and clears on submit (configurable via clearOnSubmit).
The transcription buttons (microphone, cancel, finish) are only rendered when
their corresponding event has a listener attached — for example, the
microphone button appears only when you bind @start-transcribe. The send
button, by contrast, always renders: it stays disabled until a
@submit-message listener is attached and the input is non-empty, and while
the agent is running it switches to a stop affordance that is enabled only
when a @stop listener is attached. Binding @add-file adds a file-attachment
item to the tools menu rather than a standalone button. This means you opt into
features by listening for the events you intend to handle.
Import
<script setup lang="ts">
import { CopilotChatInput } from "@copilotkit/vue/v2";
import "@copilotkit/vue/styles.css";
</script>Example
<script setup lang="ts">
import { ref } from "vue";
import { CopilotChatInput } from "@copilotkit/vue/v2";
import "@copilotkit/vue/styles.css";
const value = ref("");
function handleSubmit(text: string) {
sendMessage(text);
}
</script>
<template>
<CopilotChatInput v-model="value" @submit-message="handleSubmit" />
</template>Props
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Events
CopilotChatInput emits the following events. Bind handlers with v-on (the
@ shorthand). Several action buttons only render when their corresponding
event has a listener attached.
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Slots
CopilotChatInput exposes named slots so you can override individual parts of
the UI while keeping the component's behavior. Each slot receives scoped slot
props (event handlers, state, and labels) so your custom markup can drive the
same logic as the defaults.
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
ToolsMenuItem
The ToolsMenuItem type defines items in the tools menu. Items can trigger
actions directly or contain nested submenus.
type ToolsMenuItem = { label: string } & (
| { action: () => void; items?: never }
| { action?: never; items: (ToolsMenuItem | "-")[] }
);Prop
Type
Prop
Type
Prop
Type
Separators
Use the string "-" as an array entry to render a visual separator between menu
items:
const toolsMenu = [
{ label: "Search", action: () => search() },
"-",
{ label: "Settings", action: () => openSettings() },
];Nested Submenus
Menu items with an items array render as expandable submenus:
const toolsMenu = [
{
label: "Export",
items: [
{ label: "As PDF", action: () => exportPDF() },
{ label: "As CSV", action: () => exportCSV() },
"-",
{ label: "Print", action: () => printDoc() },
],
},
];CopilotChatAudioRecorder
A visual audio waveform component used during transcription mode. It displays a
real-time waveform visualization of the microphone input and exposes an
imperative API via a template ref.
Import
<script setup lang="ts">
import { CopilotChatAudioRecorder } from "@copilotkit/vue/v2";
</script>Imperative API (CopilotChatAudioRecorderRef)
The component exposes the following members via a template ref:
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Usage
<script setup lang="ts">
import { ref } from "vue";
import { CopilotChatAudioRecorder } from "@copilotkit/vue/v2";
// The component exposes `start()` / `stop()` via its instance type.
const recorder = ref<InstanceType<typeof CopilotChatAudioRecorder> | null>(null);
</script>
<template>
<CopilotChatAudioRecorder ref="recorder" />
<button @click="recorder?.start()">Record</button>
<button @click="recorder?.stop()">Stop</button>
</template>Usage
With Voice Transcription
<script setup lang="ts">
import { ref } from "vue";
import { CopilotChatInput } from "@copilotkit/vue/v2";
const value = ref("");
const mode = ref<"input" | "transcribe">("input");
async function handleFinishWithAudio(blob: Blob) {
value.value = await transcribeAudio(blob);
mode.value = "input";
}
</script>
<template>
<CopilotChatInput
v-model="value"
:mode="mode"
@submit-message="(text) => sendMessage(text)"
@start-transcribe="mode = 'transcribe'"
@cancel-transcribe="mode = 'input'"
@finish-transcribe-with-audio="handleFinishWithAudio"
/>
</template>With Tools Menu
<script setup lang="ts">
import { CopilotChatInput, type ToolsMenuItem } from "@copilotkit/vue/v2";
const toolsMenu: (ToolsMenuItem | "-")[] = [
{ label: "Search the web", action: () => triggerSearch() },
{ label: "Analyze data", action: () => triggerAnalysis() },
"-",
{
label: "Export",
items: [
{ label: "As PDF", action: () => exportPDF() },
{ label: "As Markdown", action: () => exportMarkdown() },
],
},
];
</script>
<template>
<CopilotChatInput
:tools-menu="toolsMenu"
@submit-message="(text) => sendMessage(text)"
/>
</template>With Stop Button During Execution
<script setup lang="ts">
import { CopilotChatInput, useAgent, useCopilotKit } from "@copilotkit/vue/v2";
const { agent } = useAgent();
const { copilotkit } = useCopilotKit();
function handleSubmit(text: string) {
if (!agent.value) return;
agent.value.addMessage({
role: "user",
content: text,
id: crypto.randomUUID(),
});
// Drive the run through CopilotKit so registered frontend tools execute and
// the run is tracked for stop/abort.
copilotkit.value.runAgent({ agent: agent.value });
}
function handleStop() {
if (!agent.value) return;
copilotkit.value.stopAgent({ agent: agent.value });
}
</script>
<template>
<CopilotChatInput
:is-running="agent?.isRunning ?? false"
auto-focus
@submit-message="handleSubmit"
@stop="handleStop"
/>
</template>Styled Slots
<script setup lang="ts">
import { CopilotChatInput } from "@copilotkit/vue/v2";
</script>
<template>
<CopilotChatInput @submit-message="(text) => sendMessage(text)">
<template #send-button="{ disabled, onClick }">
<button
:disabled="disabled"
class="rounded-full bg-green-500 px-3 py-1 text-white"
@click="onClick"
>
Send
</button>
</template>
</CopilotChatInput>
</template>Behavior
- Auto-Growing Textarea: The default textarea automatically expands as the user types, up to
maxRowsrows (default 5). After reaching the maximum, the textarea becomes scrollable. - Layout Stacking: When the input text spans multiple rows, the layout switches from inline (textarea and buttons side by side) to stacked (textarea above the control row containing the action buttons). On narrow viewports the stacked layout is used unconditionally.
- Mode Switching: Setting
modeto"transcribe"replaces the textarea with theaudio-recorderslot and automatically starts the recorder. The cancel and finish buttons replace the standard send button. Settingmodeto"processing"shows a spinner in place of the textarea. - Controlled and Uncontrolled: The component supports controlled mode (via
v-model/modelValue) and uncontrolled mode (internal state). WithclearOnSubmitenabled (the default), the input clears after each submission. - Tools Menu and Slash Commands: A non-empty
toolsMenurenders a menu button and also exposes the leaf actions as/slash commands typed inline. Use arrow keys to navigate the slash menu and Enter to run the highlighted command. - Listener-Gated Controls: The transcription buttons (microphone, cancel, finish) are only rendered when their corresponding event listener is attached. The send button always renders but stays disabled until a
@submit-messagelistener is attached and the input is non-empty; while the agent is running it switches to a stop affordance, enabled only when a@stoplistener is attached. Binding@add-fileadds a file-attachment item to the tools menu (not a standalone button). You opt into each feature by handling its event.
Related
useCopilotChatConfiguration-- Provider/composable for localized input labelsuseAgent-- Access the active agent to drive submission and stop handling