<div x-data="{ model: [] }" , class="flex flex-col items-center">
<div
x-data="autocomplete"
:data-items="[
{ text: 'spanish', value: 'spanish' },
{ text: 'french', value: 'french' },
{ text: 'portuguese', value: 'portuguese' },
{ text: 'german', value: 'german' },
{ text: 'japanese', value: 'japanese' },
{ text: 'korean', value: 'korean' },
{ text: 'italian', value: 'italian' },
{ text: 'polish', value: 'polish' },
{ text: 'greek', value: 'greek'},
{ text: 'swedish', value: 'swedish' },
{ text: 'norwegian', value: 'norwegian' },
{ text: 'danish', value: 'danish' },
{ text: 'finnish', value: 'finnish' },
{ text: 'thai', value: 'thai' },
]"
x-model="model"
class="w-[340px]"
>
<div
x-bind="trigger"
x-data="input"
class="flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
>
<div data-icon class="mr-3 empty:hidden"></div>
<div data-prepend class="mr-3 empty:hidden"></div>
<div class="relative mr-3 flex flex-1 flex-wrap overflow-hidden">
<div x-bind="selectedItems">
<template x-for="selectedItem in getSelected()">
<span
x-text="selectedItem.text"
class="mr-1 after:content-[','] last-of-type:mr-2 last-of-type:after:content-none"
></span>
</template>
</div>
<input
x-bind="input"
type="text"
class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-[64px] focus:outline-hidden"
/>
</div>
<div data-append class="mr-3 empty:hidden"></div>
<div class="flex items-center gap-x-2">
<div x-bind="loader">
<svg
viewBox="25 25 50 50"
fill="none"
class="h-5 w-5 animate-spinner-rotate"
>
<circle
cx="50"
cy="50"
r="20"
stroke="currentColor"
stroke-width="4"
stroke-miterlimit="10"
stroke-linecap="round"
class="animate-spinner-dash"
/>
</svg>
</div>
<button
x-bind="clearButton"
@click="clearSelection()"
class="flex items-center"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="pointer-events-none h-5 w-5 opacity-70"
viewBox="0 0 16 16"
>
<path
d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
/>
</svg>
</button>
<button x-bind="indicator">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 512 512"
class="h-4 w-4 rotate-180 transform duration-200"
:class="{ 'rotate-0!': isOpen }"
>
<path
d="M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z"
/>
</svg>
</button>
</div>
</div>
<div
x-bind="menu"
class="oveflow-y-auto z-50 max-h-[400px] overflow-x-hidden rounded-b-md bg-white shadow-md dark:bg-dark-800"
x-alt-transition='{
"enter": ["opacity-0 scale-y-[0.5]", "transition ease-out duration-100 origin-top", "opacity-100"],
"leave": ["opacity-100", "transition ease-in duration-100 origin-top", "opacity-0 scale-y-[0.5]"]
}'
>
<template x-if="getItems().length === 0">
<div class="px-6 py-4 text-left text-text-800 dark:text-text-300">
No items available.
</div>
</template>
<template x-for="(item, index) in getItems()" :key="item.value">
<button
x-bind="option"
class="w-full cursor-pointer scroll-mt-20 scroll-mb-20 px-6 py-4 text-left ring-secondary-400 transition-colors ring-inset focus:ring-2 focus:outline-hidden"
class-default="text-text-800 hover:bg-secondary-100 focus:bg-secondary-200 dark:text-text-300 dark:hover:bg-dark-600 dark:focus:bg-dark-600"
class-selected="bg-secondary-200 focus:bg-secondary-300 dark:bg-dark-600 dark:focus:bg-dark-700"
>
<span x-text="item.text"></span>
</button>
</template>
</div>
</div>
<div class="mx-auto mt-4 w-max font-mono text-lg">
x-model: <span x-text="JSON.stringify(model)"></span>
</div>
</div>
<script defer src="https://cdn.jsdelivr.net/npm/litewind-alpine@0.x.x/plugins/use-floating/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/litewind-alpine@0.x.x/components/autocomplete/dist/cdn.min.js"></script>
The data for the component is provided by the autocomplete
function in the x-data
directive and the props in the data-*
attributes.
The autocomplete component is based on the input component, so you can use any of its props or additional elements such as a clear button, icon, loader, and more. See the documentation for the input component here.
data-items
[]
Array
Array of items to select. This can be an array
of strings
or an array
of objects
. The objects should have at least two properties: a text
that will be displayed in the menu and a value
. This prop is optional, items can be assigned directly to the items
property.
data-item-text
text
String
Name of the property that holds the displayed text of the item.
data-item-value
value
String
Name of the property that holds the value of the item.
data-multiple
false
Boolean
Allows selecting multiple values.
data-no-filter
false
Boolean
Disables internal filtering. This is useful when data is filtered on the server.
data-no-empty-open
false
Boolean
Prevents the select menu from opening when the items array is empty. Useful for autocomplete components that filter data on the server side.
data-clearable
false
Boolean
Adds an X button that resets model to the default value.
data-use-loader
false
Boolean
Enable or disable the spinner in the input.
data-is-loading
false
Boolean
If true
displays a spinner in the input. Spinner should be first enabled in the data-use-loader
prop.
data-placeholder
empty string
String
Sets the placeholder of the input element.
x-model
[]
Array
The type of the x-model
is an array
for single and multiple modes. The values in the array
are the value
fields of the selected options.
open-selectmenu
Event dispatched after opening select menu.
scroll-to-bottom
Event dispatched after menu is scrolled to the bottom.
The data-multiple
prop enables the selection of multiple values.
<div x-data="{ model: [] }" , class="flex flex-col items-center">
<div
x-data="autocomplete"
:data-items="[
{ text: 'spanish', value: 'spanish' },
{ text: 'french', value: 'french' },
{ text: 'portuguese', value: 'portuguese' },
{ text: 'german', value: 'german' },
{ text: 'japanese', value: 'japanese' },
{ text: 'korean', value: 'korean' },
{ text: 'italian', value: 'italian' },
{ text: 'polish', value: 'polish' },
{ text: 'greek', value: 'greek'},
{ text: 'swedish', value: 'swedish' },
{ text: 'norwegian', value: 'norwegian' },
{ text: 'danish', value: 'danish' },
{ text: 'finnish', value: 'finnish' },
{ text: 'thai', value: 'thai' },
]"
data-multiple
x-model="model"
class="w-[340px]"
>
<div
x-bind="trigger"
x-data="input"
class="flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
>
<div data-icon class="mr-3 empty:hidden"></div>
<div data-prepend class="mr-3 empty:hidden"></div>
<div class="relative mr-3 flex flex-1 flex-wrap overflow-hidden">
<div x-bind="selectedItems">
<template x-for="selectedItem in getSelected()">
<span
x-text="selectedItem.text"
class="mr-1 after:content-[','] last-of-type:mr-2 last-of-type:after:content-none"
></span>
</template>
</div>
<input
x-bind="input"
type="text"
class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-[64px] focus:outline-hidden"
/>
</div>
<div data-append class="mr-3 empty:hidden"></div>
<div class="flex items-center gap-x-2">
<div x-bind="loader">
<svg
viewBox="25 25 50 50"
fill="none"
class="h-5 w-5 animate-spinner-rotate"
>
<circle
cx="50"
cy="50"
r="20"
stroke="currentColor"
stroke-width="4"
stroke-miterlimit="10"
stroke-linecap="round"
class="animate-spinner-dash"
/>
</svg>
</div>
<button
x-bind="clearButton"
@click="clearSelection()"
class="flex items-center"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="pointer-events-none h-5 w-5 opacity-70"
viewBox="0 0 16 16"
>
<path
d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
/>
</svg>
</button>
<button x-bind="indicator">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 512 512"
class="h-4 w-4 rotate-180 transform duration-200"
:class="{ 'rotate-0!': isOpen }"
>
<path
d="M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z"
/>
</svg>
</button>
</div>
</div>
<div
x-bind="menu"
class="oveflow-y-auto z-50 max-h-[400px] overflow-x-hidden rounded-b-md bg-white shadow-md dark:bg-dark-800"
x-alt-transition='{
"enter": ["opacity-0 scale-y-[0.5]", "transition ease-out duration-100 origin-top", "opacity-100"],
"leave": ["opacity-100", "transition ease-in duration-100 origin-top", "opacity-0 scale-y-[0.5]"]
}'
>
<template x-if="getItems().length === 0">
<div class="px-6 py-4 text-left text-text-800 dark:text-text-300">
No items available.
</div>
</template>
<template x-for="(item, index) in getItems()" :key="item.value">
<button
x-bind="option"
class="w-full cursor-pointer scroll-mt-20 scroll-mb-20 px-6 py-4 text-left ring-secondary-400 transition-colors ring-inset focus:ring-2 focus:outline-hidden"
class-default="text-text-800 hover:bg-secondary-100 focus:bg-secondary-200 dark:text-text-300 dark:hover:bg-dark-600 dark:focus:bg-dark-600"
class-selected="bg-secondary-200 focus:bg-secondary-300 dark:bg-dark-600 dark:focus:bg-dark-700"
>
<span x-text="item.text"></span>
</button>
</template>
</div>
</div>
<div class="mx-auto mt-4 w-max font-mono text-lg">
x-model: <span x-text="JSON.stringify(model)"></span>
</div>
</div>
The data-no-filter
disables internal filtering. This can be useful when data is filtered on the server side. In the example below, the server response replaces the items
array with the filtered results after each query. The data-no-empty-open
prop prevents opening the menu when the items array is empty.
<div
x-data="{
model: [],
isLoading: false,
filteredItems: [],
async query(q) {
if (q === '') {
this.filteredItems = []
return
}
this.isLoading = true
let res = await fetch(`https://en.wikipedia.org/w/api.php?action=query&format=json&list=prefixsearch&formatversion=2&origin=*&pslimit=50&pssearch=${q}`)
let data = await res.json()
this.filteredItems = data.query.prefixsearch
this.isLoading = false
},
}"
@update:value.debounce="query($event.detail)"
class="flex flex-col items-center"
>
<div
x-data="autocomplete"
:data-items="filteredItems"
data-no-filter
data-item-text="title"
data-item-value="pageid"
data-use-loader
:data-is-loading="isLoading"
data-no-empty-open
x-model="model"
class="w-[340px]"
>
<div
x-bind="trigger"
x-data="input"
class="flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
>
<div data-icon class="mr-3 empty:hidden"></div>
<div data-prepend class="mr-3 empty:hidden"></div>
<div class="relative mr-3 flex flex-1 flex-wrap overflow-hidden">
<div x-bind="selectedItems">
<template x-for="selectedItem in getSelected()">
<span
x-text="selectedItem.text"
class="mr-1 after:content-[','] last-of-type:mr-2 last-of-type:after:content-none"
></span>
</template>
</div>
<input
x-bind="input"
type="text"
class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-[64px] focus:outline-hidden"
/>
</div>
<div data-append class="mr-3 empty:hidden"></div>
<div class="flex items-center gap-x-2">
<div x-bind="loader">
<svg
viewBox="25 25 50 50"
fill="none"
class="h-5 w-5 animate-spinner-rotate"
>
<circle
cx="50"
cy="50"
r="20"
stroke="currentColor"
stroke-width="4"
stroke-miterlimit="10"
stroke-linecap="round"
class="animate-spinner-dash"
/>
</svg>
</div>
<button
x-bind="clearButton"
@click="clearSelection()"
class="flex items-center"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="pointer-events-none h-5 w-5 opacity-70"
viewBox="0 0 16 16"
>
<path
d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
/>
</svg>
</button>
<button x-bind="indicator">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 512 512"
class="h-4 w-4 rotate-180 transform duration-200"
:class="{ 'rotate-0!': isOpen }"
>
<path
d="M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z"
/>
</svg>
</button>
</div>
</div>
<div
x-bind="menu"
class="oveflow-y-auto z-50 max-h-[400px] overflow-x-hidden rounded-b-md bg-white shadow-md dark:bg-dark-800"
x-alt-transition='{
"enter": ["opacity-0 scale-y-[0.5]", "transition ease-out duration-100 origin-top", "opacity-100"],
"leave": ["opacity-100", "transition ease-in duration-100 origin-top", "opacity-0 scale-y-[0.5]"]
}'
>
<template x-if="getItems().length === 0">
<div class="px-6 py-4 text-left text-text-800 dark:text-text-300">
No items available.
</div>
</template>
<template x-for="(item, index) in getItems()" :key="item.value">
<button
x-bind="option"
class="w-full cursor-pointer scroll-mt-20 scroll-mb-20 px-6 py-4 text-left ring-secondary-400 transition-colors ring-inset focus:ring-2 focus:outline-hidden"
class-default="text-text-800 hover:bg-secondary-100 focus:bg-secondary-200 dark:text-text-300 dark:hover:bg-dark-600 dark:focus:bg-dark-600"
class-selected="bg-secondary-200 focus:bg-secondary-300 dark:bg-dark-600 dark:focus:bg-dark-700"
>
<span x-text="item.text"></span>
</button>
</template>
</div>
</div>
<div class="mx-auto mt-4 w-max font-mono text-lg">
x-model: <span x-text="JSON.stringify(model)"></span>
</div>
</div>
By default, the filtered portion of the option text is not highlighted as it requires x-html
directive. You can enable it by simply replacing x-text
with x-html
in the span
element of the option.
<span x-html="getHighlightedItemText"></span>
<div x-data="{ model: [] }" , class="flex flex-col items-center">
<div
x-data="autocomplete"
:data-items="[
{ text: 'spanish', value: 'spanish' },
{ text: 'french', value: 'french' },
{ text: 'portuguese', value: 'portuguese' },
{ text: 'german', value: 'german' },
{ text: 'japanese', value: 'japanese' },
{ text: 'korean', value: 'korean' },
{ text: 'italian', value: 'italian' },
{ text: 'polish', value: 'polish' },
{ text: 'greek', value: 'greek'},
{ text: 'swedish', value: 'swedish' },
{ text: 'norwegian', value: 'norwegian' },
{ text: 'danish', value: 'danish' },
{ text: 'finnish', value: 'finnish' },
{ text: 'thai', value: 'thai' },
]"
x-model="model"
class="w-[340px]"
>
<div
x-bind="trigger"
x-data="input"
class="flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
>
<div data-icon class="mr-3 empty:hidden"></div>
<div data-prepend class="mr-3 empty:hidden"></div>
<div class="relative mr-3 flex flex-1 flex-wrap overflow-hidden">
<div x-bind="selectedItems">
<template x-for="selectedItem in getSelected()">
<span
x-text="selectedItem.text"
class="mr-1 after:content-[','] last-of-type:mr-2 last-of-type:after:content-none"
></span>
</template>
</div>
<input
x-bind="input"
type="text"
class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-[64px] focus:outline-hidden"
/>
</div>
<div data-append class="mr-3 empty:hidden"></div>
<div class="flex items-center gap-x-2">
<div x-bind="loader">
<svg
viewBox="25 25 50 50"
fill="none"
class="h-5 w-5 animate-spinner-rotate"
>
<circle
cx="50"
cy="50"
r="20"
stroke="currentColor"
stroke-width="4"
stroke-miterlimit="10"
stroke-linecap="round"
class="animate-spinner-dash"
/>
</svg>
</div>
<button
x-bind="clearButton"
@click="clearSelection()"
class="flex items-center"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="pointer-events-none h-5 w-5 opacity-70"
viewBox="0 0 16 16"
>
<path
d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
/>
</svg>
</button>
<button x-bind="indicator">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 512 512"
class="h-4 w-4 rotate-180 transform duration-200"
:class="{ 'rotate-0!': isOpen }"
>
<path
d="M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z"
/>
</svg>
</button>
</div>
</div>
<div
x-bind="menu"
class="oveflow-y-auto z-50 max-h-[400px] overflow-x-hidden rounded-b-md bg-white shadow-md dark:bg-dark-800"
x-alt-transition='{
"enter": ["opacity-0 scale-y-[0.5]", "transition ease-out duration-100 origin-top", "opacity-100"],
"leave": ["opacity-100", "transition ease-in duration-100 origin-top", "opacity-0 scale-y-[0.5]"]
}'
>
<template x-if="getItems().length === 0">
<div class="px-6 py-4 text-left text-text-800 dark:text-text-300">
No items available.
</div>
</template>
<template x-for="(item, index) in getItems()" :key="item.value">
<button
x-bind="option"
class="w-full cursor-pointer scroll-mt-20 scroll-mb-20 px-6 py-4 text-left ring-secondary-400 transition-colors ring-inset focus:ring-2 focus:outline-hidden"
class-default="text-text-800 hover:bg-secondary-100 focus:bg-secondary-200 dark:text-text-300 dark:hover:bg-dark-600 dark:focus:bg-dark-600"
class-selected="bg-secondary-200 focus:bg-secondary-300 dark:bg-dark-600 dark:focus:bg-dark-700"
>
<span x-html="getHighlightedItemText"></span>
</button>
</template>
</div>
</div>
<div class="mx-auto mt-4 w-max font-mono text-lg">
x-model: <span x-text="JSON.stringify(model)"></span>
</div>
</div>