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 maxRows rows (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 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.
  • Controlled and Uncontrolled: The component supports controlled mode (via v-model / modelValue) and uncontrolled mode (internal state). With clearOnSubmit enabled (the default), the input clears after each submission.
  • Tools Menu and Slash Commands: A non-empty 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.
  • 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-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.

Related