showcase/shell-docs/src/content/reference/vue/components/CopilotChatUserMessage.mdx
CopilotChatUserMessage renders a user-authored message aligned to the right of the chat. The default message renderer flattens the message content to text and displays it with white-space: pre-wrap so line breaks and spacing are preserved.
Below the message it renders a toolbar containing a copy-to-clipboard button, an optional edit button, and optional branch navigation controls. The toolbar becomes visible on hover. The edit button is only shown when an @edit-message listener is attached, and the branch navigation is only shown when numberOfBranches is greater than 1 and a @switch-to-branch listener is attached.
<script setup lang="ts">
import { CopilotChatUserMessage } from "@copilotkit/vue/v2";
import "@copilotkit/vue/styles.css";
import type { UserMessage } from "@ag-ui/core";
const message: UserMessage = {
id: "1",
role: "user",
content: "What is the capital of France?",
};
</script>
<template>
<CopilotChatUserMessage :message="message" />
</template>
The action callbacks are emitted as Vue events. Attaching a listener for an event also enables the corresponding control: the edit button only renders when an @edit-message listener is present, and the branch navigation only renders when a @switch-to-branch listener is present (and numberOfBranches is greater than 1).
<CopilotChatUserMessage
:message="message"
@edit-message="({ message }) => openEditModal(message)"
/>
<CopilotChatUserMessage
:message="message"
:branch-index="currentBranch"
:number-of-branches="branches.length"
@switch-to-branch="({ branchIndex }) => (currentBranch = branchIndex)"
/>
Each slot exposes scoped slot props (handlers and state) and falls back to the default rendering when not provided. Use a named template (<template #slot-name="...">) to override a slot.
<CopilotChatUserMessage :message="message">
<template #message-renderer="{ content }">
<div class="font-mono text-sm">{{ content }}</div>
</template>
</CopilotChatUserMessage>
<CopilotChatUserMessage :message="message">
<template #toolbar-items>
<button @click="pin(message.id)">Pin</button>
</template>
</CopilotChatUserMessage>
<CopilotChatUserMessage
:message="message"
:branch-index="branchIndex"
:number-of-branches="3"
@switch-to-branch="onSwitch"
>
<template
#branch-navigation="{ branchIndex, numberOfBranches, canGoPrev, canGoNext, goPrev, goNext }"
>
<div class="flex items-center gap-1 text-xs">
<button :disabled="!canGoPrev" @click="goPrev">Prev</button>
<span>{{ branchIndex + 1 }} of {{ numberOfBranches }}</span>
<button :disabled="!canGoNext" @click="goNext">Next</button>
</div>
</template>
</CopilotChatUserMessage>
<script setup lang="ts">
import { CopilotChatUserMessage } from "@copilotkit/vue/v2";
import type { UserMessage } from "@ag-ui/core";
defineProps<{ message: UserMessage }>();
</script>
<template>
<CopilotChatUserMessage :message="message" />
</template>
<script setup lang="ts">
import { CopilotChatUserMessage } from "@copilotkit/vue/v2";
import type { UserMessage } from "@ag-ui/core";
defineProps<{ message: UserMessage }>();
function handleEdit(payload: { message: UserMessage }) {
console.log("Edit", payload.message.id);
}
</script>
<template>
<CopilotChatUserMessage :message="message" @edit-message="handleEdit" />
</template>
<script setup lang="ts">
import { ref } from "vue";
import { CopilotChatUserMessage } from "@copilotkit/vue/v2";
import type { UserMessage } from "@ag-ui/core";
defineProps<{ message: UserMessage; branches: UserMessage[] }>();
const currentBranch = ref(0);
function handleSwitch(payload: { branchIndex: number }) {
currentBranch.value = payload.branchIndex;
}
</script>
<template>
<CopilotChatUserMessage
:message="message"
:branch-index="currentBranch"
:number-of-branches="branches.length"
@switch-to-branch="handleSwitch"
/>
</template>
<script setup lang="ts">
import { CopilotChatUserMessage } from "@copilotkit/vue/v2";
import type { UserMessage } from "@ag-ui/core";
defineProps<{ message: UserMessage }>();
</script>
<template>
<CopilotChatUserMessage
:message="message"
@edit-message="({ message }) => console.log('Edit:', message.id)"
>
<template #message-renderer="{ content }">
<div class="rounded-2xl bg-blue-600 px-4 py-2 text-white">
{{ content }}
</div>
</template>
<template #toolbar-items>
<button @click="$emit('pin')">Pin</button>
</template>
</CopilotChatUserMessage>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { CopilotChatUserMessage } from "@copilotkit/vue/v2";
import type { UserMessage } from "@ag-ui/core";
defineProps<{ message: UserMessage; branches: UserMessage[] }>();
const currentBranch = ref(0);
function openEditModal(message: UserMessage) {
// open your edit modal
}
</script>
<template>
<CopilotChatUserMessage
:message="message"
:branch-index="currentBranch"
:number-of-branches="branches.length"
@switch-to-branch="({ branchIndex }) => (currentBranch = branchIndex)"
@edit-message="({ message }) => openEditModal(message)"
/>
</template>
message.content is an array of parts, the default renderer joins the text parts with newlines. String content is rendered as-is.white-space: pre-wrap, so line breaks and spacing are preserved. The bubble gets extra vertical padding when the content is multiline.@edit-message listener is attached.numberOfBranches is greater than 1 and a @switch-to-branch listener is attached.useCopilotChatConfiguration for available label keys.CopilotKitProvider - Supplies the CopilotKit context to descendant components.useCopilotChatConfiguration - Provider for localized toolbar labels.