Back to Copilotkit

CopilotChatInput

showcase/shell-docs/src/content/reference/vue/components/CopilotChatInput.mdx

1.61.118.2 KB
Original Source

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).

<Callout type="info"> 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. </Callout>

Import

vue
<script setup lang="ts">
import { CopilotChatInput } from "@copilotkit/vue/v2";
import "@copilotkit/vue/styles.css";
</script>

Example

vue
<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

<PropertyReference name="modelValue" type="string"> The current text value of the input. Bind with `v-model` (or pass `:model-value` together with `@update:model-value`) for controlled mode. When omitted, the component manages its own internal state (uncontrolled mode). </PropertyReference>

<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>

<PropertyReference name="disabled" type="boolean" default="false"> When `true`, the textarea and all action buttons are disabled and submission, the tools menu, and slash commands are suppressed. </PropertyReference> <PropertyReference name="placeholder" type="string"> Placeholder text for the textarea. When omitted, falls back to the `chatInputPlaceholder` label resolved from [`useCopilotChatConfiguration`](/reference/vue/hooks/useCopilotChatConfiguration). </PropertyReference> <PropertyReference name="autoFocus" type="boolean" default="true"> When `true`, the textarea receives focus automatically on mount (only in `"input"` mode). </PropertyReference> <PropertyReference name="clearOnSubmit" type="boolean" default="true"> When `true`, the input text is cleared after a message is submitted. Set to `false` to retain the text after submission. </PropertyReference> <PropertyReference name="isRunning" type="boolean" default="false"> Whether the agent is currently executing. When `true` (and not in `"transcribe"` mode), the send button is replaced with a stop button (when a `@stop` listener is present) and Enter triggers stop instead of submit. </PropertyReference> <PropertyReference name="toolsMenu" type="(ToolsMenuItem | '-')[]" default="[]"> Declarative configuration for a tools menu. Each entry is either a `ToolsMenuItem` object or a separator string `"-"`. See [ToolsMenuItem](#toolsmenuitem) for the item structure. When non-empty, a menu button is shown and the items become available as `/` slash commands inside the textarea. </PropertyReference> <PropertyReference name="maxRows" type="number" default="5"> The maximum number of rows the auto-growing textarea expands to before it becomes scrollable. </PropertyReference> <PropertyReference name="positioning" type='"static" | "absolute"' default='"static"'> Layout positioning of the input container. `"absolute"` pins the input to the bottom of its relative parent and shows the disclaimer by default. </PropertyReference> <PropertyReference name="keyboardHeight" type="number" default="0"> Pixel height of an on-screen keyboard. When greater than `0`, the container is translated upward by this amount to stay above the keyboard. </PropertyReference> <PropertyReference name="showDisclaimer" type="boolean"> Controls whether the disclaimer text is rendered below the input. When omitted, defaults to `true` only if `positioning === "absolute"`. </PropertyReference> <PropertyReference name="bottomAnchored" type="boolean" default="false"> Set to `true` when the input sits at the bottom of its container as a flex last child. Reserves bottom space for the fixed CopilotKit license banner so the two do not overlap. Not needed when `positioning === "absolute"`, which already reserves the space. </PropertyReference>

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.

<PropertyReference name="update:modelValue" type="(value: string) => void"> Emitted when the input text changes. Backs `v-model`. Bind directly with `@update:model-value` if you need the change without two-way binding. </PropertyReference> <PropertyReference name="submit-message" type="(value: string) => void"> Emitted when the user submits a message (by pressing Enter or clicking the send button). Receives the trimmed text value. The send button is only enabled when a `@submit-message` listener is present and the input is non-empty. </PropertyReference> <PropertyReference name="stop" type="() => void"> Emitted when the user clicks the stop button (or presses Enter) while `isRunning` is `true`. The send button only switches to a stop button when a `@stop` listener is present. </PropertyReference> <PropertyReference name="add-file" type="() => void"> Emitted when the user selects the file-attachment item in the tools menu. The attachment item is only added to the menu when an `@add-file` listener is present. </PropertyReference> <PropertyReference name="start-transcribe" type="() => void"> Emitted when the user clicks the microphone button to begin voice transcription. The microphone button is only rendered when a `@start-transcribe` listener is present. Typically used to switch `mode` to `"transcribe"`. </PropertyReference> <PropertyReference name="cancel-transcribe" type="() => void"> Emitted when the user cancels an in-progress voice transcription. The cancel button is only rendered during `"transcribe"` mode and when a `@cancel-transcribe` listener is present. </PropertyReference> <PropertyReference name="finish-transcribe" type="() => void"> Emitted when the user finishes voice transcription. Always emitted on finish (after `finish-transcribe-with-audio` when an audio blob is available). The finish button is only rendered during `"transcribe"` mode and when a `@finish-transcribe` listener is present. </PropertyReference>

<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>

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.

<PropertyReference name="text-area" type="slot"> Replaces the default auto-growing textarea. Scoped props include `value`, `disabled`, `placeholder`, `onInput`, `onKeydown`, `autoFocus`, `isExpanded`, `rows`, and `labels`. Only rendered in `"input"` mode. </PropertyReference> <PropertyReference name="send-button" type="slot"> Replaces the default send/stop button. Scoped props: `disabled`, `isProcessing`, and `onClick`. </PropertyReference> <PropertyReference name="add-menu-button" type="slot"> Replaces the default tools-menu trigger button. Scoped props: `disabled`, `menuOpen`, `toggleMenu`, and `labels`. </PropertyReference> <PropertyReference name="start-transcribe-button" type="slot"> Replaces the microphone button that starts transcription. Scoped props: `disabled`, `onClick`, and `labels`. Only rendered when a `@start-transcribe` listener is present. </PropertyReference> <PropertyReference name="cancel-transcribe-button" type="slot"> Replaces the button that cancels an in-progress transcription. Scoped props: `disabled`, `onClick`, and `labels`. Only rendered during `"transcribe"` mode and when a `@cancel-transcribe` listener is present. </PropertyReference> <PropertyReference name="finish-transcribe-button" type="slot"> Replaces the button that completes transcription. Scoped props: `disabled`, `onClick`, and `labels`. Only rendered during `"transcribe"` mode and when a `@finish-transcribe` listener is present. </PropertyReference> <PropertyReference name="audio-recorder" type="slot"> Replaces the audio recorder rendered during `"transcribe"` mode. Defaults to [`CopilotChatAudioRecorder`](#copilotchataudiorecorder). </PropertyReference> <PropertyReference name="disclaimer" type="slot"> Replaces the disclaimer text shown below the input. Scoped prop: `labels`. Only rendered when the disclaimer is enabled (see `showDisclaimer`). </PropertyReference> <PropertyReference name="layout" type="slot"> Replaces the entire inner layout (the input shell and all of its controls). Receives a rich set of scoped props including `mode`, `isMultiline`, `value`, `disabled`, `placeholder`, `isProcessing`, `sendDisabled`, `menuOpen`, `menuItems`, and handlers such as `onUpdateValue`, `onSubmit`, `onKeydown`, `onSendClick`, `onToggleMenu`, `onMenuAction`, `onStartTranscribe`, `onCancelTranscribe`, and `onFinishTranscribe`. Use this when you want full control over the input's structure while reusing its state machine. </PropertyReference>

ToolsMenuItem

The ToolsMenuItem type defines items in the tools menu. Items can trigger actions directly or contain nested submenus.

ts
type ToolsMenuItem = { label: string } & (
  | { action: () => void; items?: never }
  | { action?: never; items: (ToolsMenuItem | "-")[] }
);
<PropertyReference name="label" type="string" required> The display text for the menu item. </PropertyReference> <PropertyReference name="action" type="() => void"> Callback invoked when the item is selected. Mutually exclusive with `items`. </PropertyReference> <PropertyReference name="items" type="(ToolsMenuItem | '-')[]"> Nested submenu items. Each entry is either another `ToolsMenuItem` or a separator string `"-"`. Mutually exclusive with `action`. </PropertyReference>

Separators

Use the string "-" as an array entry to render a visual separator between menu items:

ts
const toolsMenu = [
  { label: "Search", action: () => search() },
  "-",
  { label: "Settings", action: () => openSettings() },
];

Nested Submenus

Menu items with an items array render as expandable submenus:

ts
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

vue
<script setup lang="ts">
import { CopilotChatAudioRecorder } from "@copilotkit/vue/v2";
</script>

Imperative API (CopilotChatAudioRecorderRef)

The component exposes the following members via a template ref:

<PropertyReference name="state" type='"idle" | "recording" | "processing"'> The current state of the recorder. </PropertyReference> <PropertyReference name="start" type="() => Promise<void>"> Begins audio recording and starts the waveform visualization. </PropertyReference> <PropertyReference name="stop" type="() => Promise<Blob>"> Stops the current recording session and resolves with the recorded audio as a `Blob`. </PropertyReference> <PropertyReference name="dispose" type="() => void"> Cleans up all resources (media stream, audio context, etc.) used by the recorder. </PropertyReference>

Usage

vue
<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

vue
<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

vue
<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

vue
<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

vue
<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.