UIInputAutoComplete
UIInputAutoComplete
A versatile input component that provides autocomplete suggestions as users type, with support for custom filtering, async loading, and multiple selection.
<template> <UIInputAutoComplete v-model="selected" :options="options" :filter="filterOptions" placeholder="Search..." @select="handleSelect" > <template #option="{ option }"> <div class="flex items-center space-x-2"> <component :is="option.icon" class="w-5 h-5 text-gray-400" /> <div> <div class="font-medium"> {{ option.label }} </div> <div class="text-sm text-gray-500"> {{ option.description }} </div> </div> </div> </template> </UIInputAutoComplete></template>
<script setup lang="ts">interface Option { value: string label: string description?: string icon?: Component}
const selected = ref('')const options = ref<Option[]>([ { value: 'vue', label: 'Vue.js', description: 'Progressive JavaScript Framework', icon: VueIcon }, { value: 'react', label: 'React', description: 'JavaScript Library for UI', icon: ReactIcon }])
const filterOptions = (query: string, option: Option) => { return option.label.toLowerCase().includes(query.toLowerCase())}
const handleSelect = (option: Option) => { console.log('Selected:', option)}</script>Props
modelValue(any): Selected valueoptions(array): Available optionsfilter(function): Custom filter functionplaceholder(string): Input placeholderdisabled(boolean): Disable inputloading(boolean): Loading statemultiple(boolean): Enable multiple selectionclearable(boolean): Allow clearing inputsearchable(boolean): Enable searchvalueKey(string): Option value keylabelKey(string): Option label keyminChars(number): Min chars for searchmaxHeight(number | string): Dropdown max heightplacement(string): Dropdown placementnoDataText(string): Empty state textloadingText(string): Loading state text
Events
update:modelValue: Value updatedselect: Option selectedclear: Value clearedsearch: Search query changedfocus: Input focusedblur: Input blurredopen: Dropdown openedclose: Dropdown closed
Slots
option: Custom option renderselected: Custom selected value renderprefix: Input prefix contentsuffix: Input suffix contentloading: Loading state contentempty: Empty state content
Usage Examples
- Basic Autocomplete:
<template> <UIInputAutoComplete v-model="selected" :options="options" placeholder="Select option" /></template>
<script setup>const selected = ref('')const options = [ { value: '1', label: 'Option 1' }, { value: '2', label: 'Option 2' }]</script>- Async Search:
<template> <UIInputAutoComplete v-model="selected" :options="searchResults" :loading="loading" :minChars="2" placeholder="Search users..." @search="handleSearch" > <template #option="{ option }"> <div class="flex items-center space-x-3"> <UIAvatar :src="option.avatar" :size="'sm'" :round="true" />
<div> <div class="font-medium"> {{ option.name }} </div> <div class="text-sm text-gray-500"> {{ option.email }} </div> </div> </div> </template>
<template #loading> <div class="flex items-center justify-center p-4"> <UISpinner size="sm" /> <span class="ml-2 text-sm text-gray-500"> Searching... </span> </div> </template>
<template #empty> <div class="p-4 text-center text-gray-500"> No users found </div> </template> </UIInputAutoComplete></template>
<script setup>const selected = ref(null)const searchResults = ref([])const loading = ref(false)
const handleSearch = async (query: string) => { if (query.length < 2) return
loading.value = true try { searchResults.value = await searchUsers(query) } finally { loading.value = false }}</script>- Multiple Selection:
<template> <UIInputAutoComplete v-model="selectedTags" :options="availableTags" :multiple="true" placeholder="Add tags" class="w-full" > <template #selected="{ selected }"> <div class="flex flex-wrap gap-2"> <UITag v-for="tag in selected" :key="tag.value" :closable="true" @close="removeTag(tag)" > <div class="flex items-center space-x-1"> <span class="w-2 h-2 rounded-full" :style="{ backgroundColor: tag.color }" /> <span>{{ tag.label }}</span> </div> </UITag> </div> </template>
<template #option="{ option, active }"> <div class="flex items-center justify-between p-2" :class="{ 'bg-primary-50': active }" > <div class="flex items-center space-x-2"> <span class="w-3 h-3 rounded-full" :style="{ backgroundColor: option.color }" /> <span>{{ option.label }}</span> </div>
<UIBadge v-if="option.count" size="sm" variant="gray" > {{ option.count }} </UIBadge> </div> </template> </UIInputAutoComplete></template>
<script setup>const selectedTags = ref([])const availableTags = [ { value: 'bug', label: 'Bug', color: '#EF4444', count: 12 }, { value: 'feature', label: 'Feature', color: '#10B981', count: 8 }]
const removeTag = (tag) => { selectedTags.value = selectedTags.value.filter(t => t.value !== tag.value)}</script>Best Practices
-
User Experience:
- Debounced search
- Clear feedback
- Keyboard navigation
- Mobile optimization
-
Performance:
- Efficient filtering
- Lazy loading
- Memory management
- Optimized rendering
-
Accessibility:
- ARIA labels
- Focus management
- Screen reader support
- Keyboard navigation
-
Mobile:
- Touch targets
- Responsive design
- Clear feedback
- Performance optimization
Common Use Cases
- User Search:
<template> <div class="space-y-2"> <label class="text-sm font-medium"> Assign To </label>
<UIInputAutoComplete v-model="assignee" :options="users" :loading="loading" placeholder="Search users..." @search="searchUsers" > <template #option="{ option }"> <div class="flex items-center space-x-3"> <UIAvatar :src="option.avatar" size="sm" :round="true" /> <div> <div class="font-medium"> {{ option.name }} </div> <div class="text-sm text-gray-500"> {{ option.role }} </div> </div> </div> </template> </UIInputAutoComplete> </div></template>- Location Search:
<template> <UIInputAutoComplete v-model="location" :options="locations" :loading="searching" placeholder="Enter location" @search="searchLocations" > <template #prefix> <MapPinIcon class="w-5 h-5 text-gray-400" /> </template>
<template #option="{ option }"> <div class="space-y-1"> <div class="font-medium"> {{ option.name }} </div> <div class="text-sm text-gray-500"> {{ option.address }} </div> </div> </template> </UIInputAutoComplete></template>- Multi-select Categories:
<template> <UIInputAutoComplete v-model="selectedCategories" :options="categories" :multiple="true" :searchable="true" placeholder="Select categories" > <template #selected="{ selected }"> <div class="flex flex-wrap gap-2"> <UITag v-for="category in selected" :key="category.value" :variant="category.color" :closable="true" @close="removeCategory(category)" > {{ category.label }} </UITag> </div> </template>
<template #option="{ option, active }"> <div class="flex items-center justify-between p-2" :class="{ 'bg-primary-50': active }" > <div class="flex items-center space-x-2"> <component :is="option.icon" class="w-5 h-5" :class="`text-${option.color}-500`" /> <span>{{ option.label }}</span> </div>
<UICheckbox :checked="isSelected(option)" class="pointer-events-none" /> </div> </template> </UIInputAutoComplete></template>Component Composition
The UIInputAutoComplete component works well with:
- UIInput for text input
- UITag for selected items
- UIAvatar for user options
- UIBadge for counts/status
- UISpinner for loading states
- UITooltip for help text
- UIIcon for visual indicators
- UICheckbox for multiple selection