showcase/shell-docs/src/content/reference/vue/components/CopilotChatInput.mdx
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).
<script setup lang="ts">
import { CopilotChatInput } from "@copilotkit/vue/v2";
import "@copilotkit/vue/styles.css";
</script>
<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>
<PropertyReference name="mode" type='"input" | "transcribe" | "processing"' default='"input"'
The current operating mode of the input component. - "input" -- Standard
text entry mode with textarea and send button. - "transcribe" -- Replaces
the textarea with the audio recorder component for voice input. -
"processing" -- Replaces the textarea with a loading spinner to indicate
background processing is underway.
</PropertyReference>
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.
<PropertyReference name="finish-transcribe-with-audio" type="(audioBlob: Blob) => void"
Emitted when the user finishes voice transcription and a recording is
available. Receives the recorded audio as a Blob. Use this when you need to
send the raw audio for server-side speech-to-text processing.
</PropertyReference>
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.
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 | "-")[] }
);
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() },
];
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() },
],
},
];
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.
<script setup lang="ts">
import { CopilotChatAudioRecorder } from "@copilotkit/vue/v2";
</script>
The component exposes the following members via a template ref:
<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>
<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>
<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>
<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>
<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>
maxRows rows (default 5). After reaching the maximum, the textarea becomes scrollable.mode to "transcribe" replaces the textarea with the audio-recorder slot and automatically starts the recorder. The cancel and finish buttons replace the standard send button. Setting mode to "processing" shows a spinner in place of the textarea.v-model / modelValue) and uncontrolled mode (internal state). With clearOnSubmit enabled (the default), the input clears after each submission.toolsMenu renders 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.@submit-message listener is attached and the input is non-empty; while the agent is running it switches to a stop affordance, enabled only when a @stop listener is attached. Binding @add-file adds a file-attachment item to the tools menu (not a standalone button). You opt into each feature by handling its event.useCopilotChatConfiguration -- Provider/composable for localized input labelsuseAgent -- Access the active agent to drive submission and stop handling