Usage
The ChatPrompt component renders a <form> element and extends the Textarea component so you can pass any property such as icon, placeholder, autofocus, etc.
- The form is submitted when the user presses ↵ or when the user clicks on the submit button.
- The textarea is blurred when Esc is pressed and emits a
closeevent.
Variant
Use the variant prop to change the style of the prompt. Defaults to outline.
<template>
<UChatPrompt variant="soft" />
</template>
Examples
Within a page
Use the ChatPrompt component with the Chat class from AI SDK v5 to display a chat prompt within a page.
Pass the input prop alongside the error prop to disable the textarea when an error occurs.
<script setup lang="ts">
import { Chat } from '@ai-sdk/vue'
const input = ref('')
const chat = new Chat({
onError(error) {
console.error(error)
}
})
function onSubmit() {
chat.sendMessage({ text: input.value })
input.value = ''
}
</script>
<template>
<UDashboardPanel>
<template #body>
<UContainer>
<UChatMessages :messages="chat.messages" :status="chat.status">
<template #content="{ message }">
<template v-for="(part, index) in message.parts" :key="`${message.id}-${part.type}-${index}`">
<MDC v-if="part.type === 'text' && message.role === 'assistant'" :value="part.text" :cache-key="`${message.id}-${index}`" class="*:first:mt-0 *:last:mb-0" />
<p v-else-if="part.type === 'text' && message.role === 'user'" class="whitespace-pre-wrap">{{ part.text }}</p>
</template>
</template>
</UChatMessages>
</UContainer>
</template>
<template #footer>
<UContainer class="pb-4 sm:pb-6">
<UChatPrompt v-model="input" :error="chat.error" @submit="onSubmit">
<UChatPromptSubmit :status="chat.status" @stop="chat.stop()" @reload="chat.regenerate()" />
</UChatPrompt>
</UContainer>
</template>
</UDashboardPanel>
</template>
You can also use it as a starting point for a chat interface.
<script setup lang="ts">
import { Chat } from '@ai-sdk/vue'
const input = ref('')
const chat = new Chat()
async function onSubmit() {
chat.sendMessage({ text: input.value })
// Navigate to chat page after first message
if (chat.messages.length === 1) {
await navigateTo('/chat')
}
}
</script>
<template>
<UDashboardPanel>
<template #body>
<UContainer>
<h1>How can I help you today?</h1>
<UChatPrompt v-model="input" @submit="onSubmit">
<UChatPromptSubmit :status="chat.status" />
</UChatPrompt>
</UContainer>
</template>
</UDashboardPanel>
</template>
API
Props
| Prop | Default | Type |
|---|---|---|
as | 'form' | anyThe element or component this component should render as. |
placeholder | t('chatPrompt.placeholder') | stringThe placeholder text for the textarea. |
variant | 'outline' | "outline" | "soft" | "subtle" | "naked" |
error | Error | |
autofocus | true | boolean |
disabled | boolean | |
icon | anyDisplay an icon based on the | |
avatar | AvatarPropsDisplay an avatar on the left side.
| |
loading | boolean When | |
loadingIcon | appConfig.ui.icons.loading | anyThe icon when the |
rows | 1 | number |
autofocusDelay | number | |
autoresize | true | boolean |
autoresizeDelay | number | |
maxrows | number | |
modelValue | '' | string |
ui | { root?: ClassNameValue; header?: ClassNameValue; body?: ClassNameValue; footer?: ClassNameValue; base?: ClassNameValue; } & { root?: ClassNameValue; base?: ClassNameValue; leading?: ClassNameValue; leadingIcon?: ClassNameValue; leadingAvatar?: ClassNameValue; leadingAvatarSize?: ClassNameValue; trailing?: ClassNameValue; trailingIcon?: ClassNameValue; }
|
Slots
| Slot | Type |
|---|---|
header | {} |
footer | {} |
leading | { ui: { root: (props?: Record<string, any> | undefined) => string; base: (props?: Record<string, any> | undefined) => string; leading: (props?: Record<string, any> | undefined) => string; leadingIcon: (props?: Record<string, any> | undefined) => string; leadingAvatar: (props?: Record<string, any> | undefined) => string; leadingAvatarSize: (props?: Record<string, any> | undefined) => string; trailing: (props?: Record<string, any> | undefined) => string; trailingIcon: (props?: Record<string, any> | undefined) => string; }; } |
default | { ui: { root: (props?: Record<string, any> | undefined) => string; base: (props?: Record<string, any> | undefined) => string; leading: (props?: Record<string, any> | undefined) => string; leadingIcon: (props?: Record<string, any> | undefined) => string; leadingAvatar: (props?: Record<string, any> | undefined) => string; leadingAvatarSize: (props?: Record<string, any> | undefined) => string; trailing: (props?: Record<string, any> | undefined) => string; trailingIcon: (props?: Record<string, any> | undefined) => string; }; } |
trailing | { ui: { root: (props?: Record<string, any> | undefined) => string; base: (props?: Record<string, any> | undefined) => string; leading: (props?: Record<string, any> | undefined) => string; leadingIcon: (props?: Record<string, any> | undefined) => string; leadingAvatar: (props?: Record<string, any> | undefined) => string; leadingAvatarSize: (props?: Record<string, any> | undefined) => string; trailing: (props?: Record<string, any> | undefined) => string; trailingIcon: (props?: Record<string, any> | undefined) => string; }; } |
Emits
| Event | Type |
|---|---|
close | [event: Event] |
update:modelValue | [value: string] |
submit | [event: Event] |
Expose
When accessing the component via a template ref, you can use the following:
| Name | Type |
|---|---|
textareaRef | Ref<HTMLTextAreaElement | null> |
Theme
export default defineAppConfig({
ui: {
chatPrompt: {
slots: {
root: 'relative flex flex-col items-stretch gap-2 px-2.5 py-2 w-full rounded-lg backdrop-blur',
header: 'flex items-center gap-1.5',
body: 'items-start',
footer: 'flex items-center justify-between gap-1.5',
base: 'text-base/5'
},
variants: {
variant: {
outline: {
root: 'bg-default/75 ring ring-default'
},
soft: {
root: 'bg-elevated/50'
},
subtle: {
root: 'bg-elevated/50 ring ring-default'
},
naked: {
root: ''
}
}
},
defaultVariants: {
variant: 'outline'
}
}
}
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
chatPrompt: {
slots: {
root: 'relative flex flex-col items-stretch gap-2 px-2.5 py-2 w-full rounded-lg backdrop-blur',
header: 'flex items-center gap-1.5',
body: 'items-start',
footer: 'flex items-center justify-between gap-1.5',
base: 'text-base/5'
},
variants: {
variant: {
outline: {
root: 'bg-default/75 ring ring-default'
},
soft: {
root: 'bg-elevated/50'
},
subtle: {
root: 'bg-elevated/50 ring ring-default'
},
naked: {
root: ''
}
}
},
defaultVariants: {
variant: 'outline'
}
}
}
})
]
})