Sections
Components
- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Code Block
- Collapsible
- Combobox
- Command
- Context Menu
- Data Table
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Form
- Hover Card
- Input
- Input Group
- Input OTP
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Native Select
- Navigation Menu
- Number Field
- Pagination
- Pin Input
- Popover
- Progress
- Questionnaire
- Radio Group
- Range Calendar
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Stepper
- Switch
- Table
- Tabs
- Tags Input
- Textarea
- Toast
- Toggle
- Toggle Group
- Tooltip
- Trace
- Typography
AI Elements
Directory
Forms
Utilities
<script setup lang="ts">
import { ArrowUpIcon, PlusIcon } from '@lucide/vue'
import { computed, ref } from 'vue'
import { Bubble, BubbleContent } from '@/components/ui/bubble'
import { Button } from '@/components/ui/button'
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupTextarea,
} from '@/components/ui/input-group'
import { Message, MessageContent } from '@/components/ui/message'
const suggestions = [
'Add a dark mode toggle',
'Fix the failing build',
'Write tests for the form',
'Upgrade to Nuxt 4',
'Add a loading state',
'Find unused dependencies',
]
const replies: Record<string, string> = {
'Add a dark mode toggle':
'Added a ModeToggle to the site header. It calls useColorMode from @vueuse/core.',
'Fix the failing build':
'The build failed on a missing import in pages/index.vue. I added it and the build passes.',
'Write tests for the form':
'Added four tests in SignupForm.test.ts. They cover the empty, invalid and valid states.',
'Upgrade to Nuxt 4':
'Ran the codemod and bumped nuxt to 4. Two pages needed the new app/ directory, so I moved them.',
'Add a loading state':
'Added a loading state to the dashboard page. It shows skeletons while the data loads.',
'Find unused dependencies':
'Three packages are not imported anywhere: lodash, moment and classnames. Should I remove them?',
}
const text = ref('')
const turn = ref<{ prompt: string, reply: string } | null>(null)
const formRef = ref<HTMLFormElement | null>(null)
const canSend = computed(() => text.value.trim().length > 0)
function send(prompt: string) {
turn.value = {
prompt,
reply:
replies[prompt] ?? 'This demo only knows the suggestions. Pick one.',
}
text.value = ''
}
function onSubmit() {
if (!canSend.value)
return
send(text.value.trim())
}
function onKeydown(event: KeyboardEvent) {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault()
formRef.value?.requestSubmit()
}
}
</script>
<template>
<div class="mx-auto flex w-full max-w-lg flex-col gap-6 py-6 text-sm">
<div v-if="turn" class="flex flex-col gap-4">
<Message align="end">
<MessageContent>
<Bubble align="end" variant="muted">
<BubbleContent>{{ turn.prompt }}</BubbleContent>
</Bubble>
</MessageContent>
</Message>
<Message>
<MessageContent>
<p>{{ turn.reply }}</p>
</MessageContent>
</Message>
</div>
<div class="flex flex-col gap-2">
<div class="flex scroll-fade-x scrollbar-none gap-2 overflow-x-auto overscroll-x-contain">
<Button
v-for="suggestion in suggestions"
:key="suggestion"
variant="outline"
size="sm"
class="shrink-0"
@click="send(suggestion)"
>
{{ suggestion }}
</Button>
</div>
<form ref="formRef" @submit.prevent="onSubmit">
<InputGroup>
<InputGroupTextarea
v-model="text"
placeholder="Ask the agent to change your code..."
class="p-3"
@keydown="onKeydown"
/>
<InputGroupAddon align="block-end">
<InputGroupButton
size="icon-sm"
variant="outline"
class="rounded-full"
aria-label="Attach files"
>
<PlusIcon />
</InputGroupButton>
<InputGroupButton
type="submit"
size="icon-sm"
variant="default"
class="ml-auto rounded-full"
:disabled="!canSend"
aria-label="Send"
>
<ArrowUpIcon />
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
</form>
</div>
</div>
</template>Suggestion is a prompt you send without typing. Each one is a Button with the prompt as its label. Put a row of them above the Prompt Input.
Installation
Suggestion is built with the Button component.
pnpm dlx shadcn-vue@latest add button
Usage
<script setup lang="ts">
import { Button } from '@/components/ui/button'
</script>
<template>
<div class="scrollbar-none flex scroll-fade-x gap-2 overflow-x-auto overscroll-x-contain">
<Button
v-for="suggestion in suggestions"
:key="suggestion"
variant="outline"
size="sm"
class="shrink-0"
@click="send(suggestion)"
>
{{ suggestion }}
</Button>
</div>
</template>Scrolling
The row scrolls sideways when the suggestions do not fit. scroll-fade-x fades the edge that has more to show, and shrink-0 keeps each label on one line.
AI SDK
The label is the message. Send it as is.
chat.sendMessage({ text: suggestion })Migrating from AI Elements Vue
If you added Suggestion from AI Elements Vue, your project has a components/ai-elements/suggestion/ folder. Paste this prompt into your coding agent to rebuild it on Button. Every export keeps its name and props, so the code that uses it keeps working.
Migrate components/ai-elements/suggestion/ from AI Elements Vue to the shadcn-vue Button component.
Keep every export, with the same name and props, so the code that imports it keeps working. Only change how each part renders.
1. Install Button: npx shadcn-vue@latest add button
2. Read https://www.shadcn-vue.com/docs/ai-elements/suggestion and build Suggestion the same way.
3. Rebuild each part:
- Suggestions renders a div with class="flex scroll-fade-x scrollbar-none gap-2 overflow-x-auto overscroll-x-contain", instead of a ScrollArea. Change its props to a class prop and pass the slot through.
- Suggestion renders a Button with variant="outline", size="sm" and class="shrink-0". Keep the click emit, called with the suggestion.
4. Remove the imports the files no longer use.
5. Run vue-tsc and fix any errors.Sponsors
Your logo hereshadcn-vue is free and open source. Sponsorship keeps it maintained.
Become a sponsor