Prompt Input

Allows a user to send a message with file attachments to a large language model. It includes a textarea, file upload capabilities, a submit button, and a dropdown for selecting the model.

The PromptInput component allows a user to send a message with file attachments to a large language model. It includes a textarea, file upload capabilities, a submit button, and a dropdown for selecting the model.

Install using CLI

AI Elements Vue
shadcn-vue CLI
npx ai-elements-vue@latest add prompt-input

Install Manually

Copy and paste the following code in the same folder.

PromptInputProvider.vue
PromptInput.vue
PromptInputTextarea.vue
PromptInputTools.vue
PromptInputButton.vue
PromptInputSubmit.vue
PromptInputBody.vue
PromptInputAttachments.vue
PromptInputAttachment.vue
PromptInputHeader.vue
PromptInputFooter.vue
PromptInputActionMenu.vue
PromptInputActionMenuTrigger.vue
PromptInputActionMenuContent.vue
PromptInputActionMenuItem.vue
PromptInputActionAddAttachments.vue
PromptInputSpeechButton.vue
PromptInputSelect.vue
PromptInputSelectTrigger.vue
PromptInputSelectContent.vue
PromptInputSelectItem.vue
PromptInputSelectValue.vue
PromptInputHoverCard.vue
PromptInputHoverCardTrigger.vue
PromptInputHoverCardContent.vue
PromptInputTabsList.vue
PromptInputTab.vue
PromptInputTabLabel.vue
PromptInputTabBody.vue
PromptInputTabItem.vue
PromptInputCommand.vue
PromptInputCommandInput.vue
PromptInputCommandList.vue
PromptInputCommandEmpty.vue
PromptInputCommandGroup.vue
PromptInputCommandItem.vue
PromptInputCommandSeparator.vue
context.ts
types.ts
index.ts
<script setup lang="ts">
import type { PromptInputMessage } from './types'
import { usePromptInputProvider } from './context'

const props = defineProps<{
  initialInput?: string
  maxFiles?: number
  maxFileSize?: number
  accept?: string
}>()

const emit = defineEmits<{
  (e: 'submit', payload: PromptInputMessage): void
  (e: 'error', payload: { code: string, message: string }): void
}>()

usePromptInputProvider({
  initialInput: props.initialInput,
  maxFiles: props.maxFiles,
  maxFileSize: props.maxFileSize,
  accept: props.accept,
  onSubmit: msg => emit('submit', msg),
  onError: err => emit('error', err),
})
</script>

<template>
  <slot />
</template>

Usage with AI SDK

Built a fully functional chat app using PromptInput, Conversation with a model picker:

Add the following component to your frontend:

pages/index.vue
<script setup lang="ts">
import type { PromptInputMessage } from '@/components/ai-elements/prompt-input'
import { useChat } from '@ai-sdk/vue'
import { GlobeIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import {
  Conversation,
  ConversationContent,
  ConversationScrollButton,
} from '@/components/ai-elements/conversation'
import { Message, MessageContent, MessageResponse } from '@/components/ai-elements/message'
import {
  PromptInput,
  PromptInputActionAddAttachments,
  PromptInputActionMenu,
  PromptInputActionMenuContent,
  PromptInputActionMenuTrigger,
  PromptInputAttachment,
  PromptInputAttachments,
  PromptInputBody,
  PromptInputButton,
  PromptInputFooter,
  PromptInputHeader,
  PromptInputSelect,
  PromptInputSelectContent,
  PromptInputSelectItem,
  PromptInputSelectTrigger,
  PromptInputSelectValue,
  PromptInputSpeechButton,
  PromptInputSubmit,
  PromptInputTextarea,
  PromptInputTools
} from '@/components/ai-elements/prompt-input'

const models = [
  { id: 'gpt-4o', name: 'GPT-4o' },
  { id: 'claude-opus-4-20250514', name: 'Claude 4 Opus' },
]

const text = ref<string>('')
const model = ref<string>(models[0].id)
const useWebSearch = ref<boolean>(false)
const textareaRef = ref<HTMLTextAreaElement | null>(null)

const { messages, status, sendMessage } = useChat()

function handleSubmit(message: PromptInputMessage) {
  const hasText = Boolean(message.text)
  const hasAttachments = Boolean(message.files?.length)

  if (!(hasText || hasAttachments)) {
    return
  }

  sendMessage(
    {
      text: message.text || 'Sent with attachments',
      files: message.files
    },
    {
      body: {
        model: model.value,
        webSearch: useWebSearch.value,
      },
    },
  )

  text.value = ''
}
</script>

<template>
  <div class="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]">
    <div class="flex flex-col h-full">
      <Conversation>
        <ConversationContent>
          <Message
            v-for="message in messages"
            :key="message.id"
            :from="message.role"
          >
            <MessageContent>
              <template v-for="(part, i) in message.parts" :key="`${message.id}-${i}`">
                <MessageResponse v-if="part.type === 'text'">
                  {{ part.text }}
                </MessageResponse>
              </template>
            </MessageContent>
          </Message>
        </ConversationContent>
        <ConversationScrollButton />
      </Conversation>

      <PromptInput
        class="mt-4"
        global-drop
        multiple
        @submit="handleSubmit"
      >
        <PromptInputHeader>
          <PromptInputAttachments>
            <template #default="{ attachment }">
              <PromptInputAttachment :data="attachment" />
            </template>
          </PromptInputAttachments>
        </PromptInputHeader>

        <PromptInputBody>
          <PromptInputTextarea
            ref="textareaRef"
            :model-value="text"
            @update:model-value="text = $event"
          />
        </PromptInputBody>

        <PromptInputFooter>
          <PromptInputTools>
            <PromptInputActionMenu>
              <PromptInputActionMenuTrigger />
              <PromptInputActionMenuContent>
                <PromptInputActionAddAttachments />
              </PromptInputActionMenuContent>
            </PromptInputActionMenu>

            <PromptInputSpeechButton
              :textarea-ref="textareaRef?.$el || null"
              @transcription-change="text = $event"
            />

            <PromptInputButton
              :variant="useWebSearch ? 'default' : 'ghost'"
              @click="useWebSearch = !useWebSearch"
            >
              <GlobeIcon :size="16" />
              <span>Search</span>
            </PromptInputButton>

            <PromptInputSelect v-model="model">
              <PromptInputSelectTrigger>
                <PromptInputSelectValue />
              </PromptInputSelectTrigger>
              <PromptInputSelectContent>
                <PromptInputSelectItem
                  v-for="m in models"
                  :key="m.id"
                  :value="m.id"
                >
                  {{ m.name }}
                </PromptInputSelectItem>
              </PromptInputSelectContent>
            </PromptInputSelect>
          </PromptInputTools>

          <PromptInputSubmit
            :disabled="!text && !status"
            :status="status"
          />
        </PromptInputFooter>
      </PromptInput>
    </div>
  </div>
</template>

Add the following route to your backend:

server/api/chat.ts
import { convertToModelMessages, streamText, UIMessage } from 'ai'

// Allow streaming responses up to 30 seconds
export const maxDuration = 30

export default defineEventHandler(async (event) => {
  const {
    model,
    messages,
    webSearch
  }: {
    messages: UIMessage[]
    model: string
    webSearch?: boolean
  } = await readBody(event)

  const result = streamText({
    model: webSearch ? 'perplexity/sonar' : model,
    messages: convertToModelMessages(messages),
  })

  return result.toUIMessageStreamResponse()
})

Features

  • Auto-resizing textarea that adjusts height based on content
  • File attachment support with drag-and-drop
  • Image preview for image attachments
  • Configurable file constraints (max files, max size, accepted types)
  • Automatic submit button icons based on status
  • Support for keyboard shortcuts (Enter to submit, Shift+Enter for new line)
  • Customizable min/max height for the textarea
  • Flexible toolbar with support for custom actions and tools
  • Built-in model selection dropdown
  • Built-in native speech recognition button (Web Speech API)
  • Optional provider for lifted state management
  • Form automatically resets on submit
  • Responsive design with mobile-friendly controls
  • Clean, modern styling with customizable themes
  • Form-based submission handling
  • Hidden file input sync for native form posts
  • Global document drop support (opt-in)

Examples

Cursor Style

Props

<PromptInputProvider />

initialInputstring
Initial text input value.

<PromptInput />

acceptstring
File types to accept (e.g., "image/*"). Leave undefined for any.
multipleboolean
Whether to allow multiple file selection.
globalDropboolean
When true, accepts file drops anywhere on the document.
maxFilesnumber
Maximum number of files allowed.
maxFileSizenumber
Maximum size of files in bytes.
...propsHTMLAttributes
Additional props are spread to the root form element.

<PromptInputTextarea />

...propsInstanceType<typeof InputGroupTextarea>
Any other props are spread to the underlying Textarea component.

<PromptInputTools />

...propsHTMLAttributes
Additional props are spread to the underlying div.

<PromptInputButton />

variant'default' | 'secondary' | 'destructive' | 'outline' | 'ghost' | 'link'
'ghost'
Visual style of the button.
size'default' | 'sm' | 'lg' | 'icon'
auto
Button size. Defaults to 'icon' when a single child is provided, otherwise 'default'.
...propsInstanceType<typeof InputGroupButton>
Additional props are spread to the underlying InputGroupButton component.

<PromptInputSubmit />

statusChatStatus
Current chat status; controls the icon (submitted, streaming, error, ready).
variant'default' | 'secondary' | 'destructive' | 'outline' | 'ghost' | 'link'
'default'
Visual style of the submit button.
size'default' | 'sm' | 'lg' | 'icon'
'icon'
Size of the submit button.
...propsInstanceType<typeof InputGroupButton>
Additional props are spread to the underlying InputGroupButton component.

<PromptInputBody />

...propsHTMLAttributes
Any other props are spread to the underlying body div.

<PromptInputAttachments />

...propsHTMLAttributes
Any other props are spread to the underlying attachments container.

<PromptInputAttachment />

fileAttachmentFile
The attachment data to display.
...propsHTMLAttributes
Any other props are spread to the underlying attachment div.

<PromptInputHeader />

...props InstanceType<typeof InputGroupAddon>
Any other props (except align) are spread to the underlying InputGroupAddon component.

<PromptInputFooter />

...props InstanceType<typeof InputGroupAddon>
Any other props (except align) are spread to the underlying InputGroupAddon component.

<PromptInputActionMenu />

...props InstanceType<typeof DropdownMenu>
Any other props are spread to the underlying DropdownMenu component.

<PromptInputActionMenuTrigger />

...props InstanceType<typeof DropdownMenuTrigger>
Any other props are spread to the underlying DropdownMenuTrigger component.

<PromptInputActionMenuContent />

...props InstanceType<typeof DropdownMenuContent>
Any other props are spread to the underlying DropdownMenuContent component.

<PromptInputActionMenuItem />

...props InstanceType<typeof DropdownMenuItem>
Any other props are spread to the underlying DropdownMenuItem component.

<PromptInputActionAddAttachments />

labelstring
Label for the menu item.
...props InstanceType<typeof DropdownMenuItem>
Any other props are spread to the underlying DropdownMenuItem component.

<PromptInputSpeechButton />

...props InstanceType<typeof PromptInputButton >
Any other props are spread to the underlying PromptInputButton component.

<PromptInputSelect />

...props InstanceType<typeof Select>
Any other props are spread to the underlying Select component.

<PromptInputSelectTrigger />

...propsInstanceType<typeof SelectTrigger>
Any other props are spread to the underlying SelectTrigger component.

<PromptInputSelectContent />

...propsInstanceType<typeof SelectContent>
Any other props are spread to the underlying SelectContent component.

<PromptInputSelectItem />

...propsInstanceType<typeof SelectItem>
Any other props are spread to the underlying SelectItem component.

<PromptInputSelectValue />

...propsInstanceType<typeof SelectValue>
Any other props are spread to the underlying SelectValue component.

<PromptInputHoverCard />

openDelaynumber
0
Delay in milliseconds before opening.
closeDelaynumber
0
Delay in milliseconds before closing.
...propsInstanceType<typeof HoverCard>
Any other props are spread to the HoverCard component.

<PromptInputHoverCardTrigger />

...propsInstanceType<typeof HoverCardTrigger>
Any other props are spread to the HoverCardTrigger component.

<PromptInputHoverCardContent />

align'start' | 'center' | 'end'
'start'
Alignment of the hover card content.
...propsInstanceType<typeof HoverCardContent>
Any other props are spread to the HoverCardContent component.

<PromptInputTabsList />

...propsHTMLAttributes
Any other props are spread to the div element.

<PromptInputTab />

...propsHTMLAttributes
Any other props are spread to the div element.

<PromptInputTabLabel />

...propsHTMLAttributes
Any other props are spread to the h3 element.

<PromptInputTabBody />

...propsHTMLAttributes
Any other props are spread to the div element.

<PromptInputTabItem />

...propsHTMLAttributes
Any other props are spread to the div element.

<PromptInputCommand />

...propsInstanceType<typeof Command>
Any other props are spread to the Command component.

<PromptInputCommandInput />

...propsInstanceType<typeof CommandInput>
Any other props are spread to the CommandInput component.

<PromptInputCommandList />

...propsInstanceType<typeof CommandList>
Any other props are spread to the CommandList component.

<PromptInputCommandEmpty />

...propsInstanceType<typeof CommandEmpty>
Any other props are spread to the CommandEmpty component.

<PromptInputCommandGroup />

...propsInstanceType<typeof CommandGroup>
Any other props are spread to the CommandGroup component.

<PromptInputCommandItem />

...propsInstanceType<typeof CommandItem>
Any other props are spread to the CommandItem component.

<PromptInputCommandSeparator />

...propsInstanceType<typeof CommandSeparator>
Any other props are spread to the CommandSeparator component.