Skip to content

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 value
  • options (array): Available options
  • filter (function): Custom filter function
  • placeholder (string): Input placeholder
  • disabled (boolean): Disable input
  • loading (boolean): Loading state
  • multiple (boolean): Enable multiple selection
  • clearable (boolean): Allow clearing input
  • searchable (boolean): Enable search
  • valueKey (string): Option value key
  • labelKey (string): Option label key
  • minChars (number): Min chars for search
  • maxHeight (number | string): Dropdown max height
  • placement (string): Dropdown placement
  • noDataText (string): Empty state text
  • loadingText (string): Loading state text

Events

  • update:modelValue: Value updated
  • select: Option selected
  • clear: Value cleared
  • search: Search query changed
  • focus: Input focused
  • blur: Input blurred
  • open: Dropdown opened
  • close: Dropdown closed

Slots

  • option: Custom option render
  • selected: Custom selected value render
  • prefix: Input prefix content
  • suffix: Input suffix content
  • loading: Loading state content
  • empty: Empty state content

Usage Examples

  1. 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>
  1. 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>
  1. 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

  1. User Experience:

    • Debounced search
    • Clear feedback
    • Keyboard navigation
    • Mobile optimization
  2. Performance:

    • Efficient filtering
    • Lazy loading
    • Memory management
    • Optimized rendering
  3. Accessibility:

    • ARIA labels
    • Focus management
    • Screen reader support
    • Keyboard navigation
  4. Mobile:

    • Touch targets
    • Responsive design
    • Clear feedback
    • Performance optimization

Common Use Cases

  1. 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>
  1. 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>
  1. 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