Component

Vue Autocomplete Component

<dom-autocomplete>

A styled datalist-like text input: free text stays separate from selected suggestions.

Playground

Try every prop live

Autocomplete playground

Edit props in the inspector — type to filter suggestions and test the free-text path.

Playground.vuevue
Install
npm install @getdom/studio
vue
<script setup>
import '@getdom/studio/style.css';
import { reactive } from 'vue';
import { DomAutocomplete } from '@getdom/studio';

const data = reactive({
		"modelValue": "",
		"id": "",
		"name": "",
		"label": "",
		"description": "",
		"placeholder": "Search people",
		"required": false,
		"disabled": false,
		"readOnly": false,
		"invalid": false,
		"errors": [],
		"visible": true,
		"validators": [],
		"validateOnBlur": true,
		"chrome": "field",
		"options": [
			{
				"value": "1",
				"label": "Ada Lovelace",
				"email": "ada@example.com"
			},
			{
				"value": "2",
				"label": "Grace Hopper",
				"email": "grace@example.com"
			},
			{
				"value": "3",
				"label": "Katherine Johnson",
				"email": "katherine@example.com"
			}
		],
		"placement": "bottom",
		"floatingMode": "viewport",
		"loading": false,
		"filterOptions": true,
		"hasMore": false
	});
</script>

<template>
	<DomAutocomplete
		v-bind="data"
		@update:modelValue="data.modelValue = $event"
	/>
</template>

Demo

Search people

v-model is the typed text. @select receives the original option object when a suggestion is chosen.

Typed text: —

Selected item: —

Install
npm install @getdom/studio
vue
<script setup>
import '@getdom/studio/style.css';
import { DomAutocomplete } from '@getdom/studio';
import { ref } from 'vue';

const text = ref('');
const selected = ref(null);
const people = [
	{ value: '1', label: 'Ada Lovelace', email: 'ada@example.com' },
	{ value: '2', label: 'Grace Hopper', email: 'grace@example.com' },
	{ value: '3', label: 'Katherine Johnson', email: 'katherine@example.com' },
];

function onSelect(event) {
	selected.value = event.item;
}
</script>

<template>
	<div class="flex w-full max-w-sm flex-col gap-2">
		<DomAutocomplete
			v-model="text"
			:options="people"
			placeholder="Search people"
			class="w-full"
			@select="onSelect"
		/>
		<p class="text-xs text-muted-fg">Typed text: <code class="text-canvas-fg">{{ text || '—' }}</code></p>
		<p class="text-xs text-muted-fg">Selected item: <code class="text-canvas-fg">{{ selected?.email || '—' }}</code></p>
	</div>
</template>

Demo

Server-loaded suggestions

@query lets the parent fetch suggestions and pass the current server results back through options.

Browse paginated suggestions or commit your own text.

0 of 0 results loaded. Scroll for more; search resets the pages.

Value: None

Install
npm install @getdom/studio
vue
<script setup>
import '@getdom/studio/style.css';
import { ref } from 'vue';
import { DomAutocomplete } from '@getdom/studio';
import { usePagedLookup } from '../../_shared/usePagedLookup.js';
import LookupStatus from '../../_shared/LookupStatus.vue';

const value = ref('');
const { options, loading, hasMore, error, total, search, loadMore } = usePagedLookup('people');
</script>

<template>
	<div class="grid w-full max-w-md gap-3">
		<DomAutocomplete
			v-model="value"
			:options="options"
			:loading="loading"
			:has-more="hasMore"
			:filter-options="false"
			label="Search people"
			description="Browse paginated suggestions or commit your own text."
			placeholder="Search or browse people"
			@query="search"
			@load-more="loadMore"
		>
			<template #item="{ item }">
				<span class="block truncate font-medium">{{ item.label }}</span>
				<span class="block truncate text-xs text-muted-fg">{{ item.email }} · {{ item.role }}</span>
			</template>
		</DomAutocomplete>
		<LookupStatus :error="error" :count="options.length" :total="total" @retry="loadMore" />
		<p class="text-xs text-muted-fg">Value: <code>{{ value || 'None' }}</code></p>
	</div>
</template>

Usage

Plain HTML

Use the headless custom element when you want autocomplete in plain HTML or another framework. The headless page includes a copyable HTML and JavaScript example.

View headless autocomplete

Behaviour

Selection, search and remote pages

A selected value is not a search filter. Opening shows the loaded options and a checkmark on matching selections. Only typing filters; custom item/option slots receive query and selected.

Set :filter-options="false" for server-ranked results. Handle @query to reset the cursor and replace options, then @load-more to append the next page. Pass loading and hasMore. Scrolling, keyboard navigation at the last row, and the Load more button request another page.

The examples call /api/form-options, a real HTTP endpoint serving synthetic records. The shared example loader debounces queries, aborts old requests, ignores stale responses, deduplicates pages and exposes a Retry button. Applications own their endpoint, authorization and cursor protocol; the controls do not fetch data themselves.

Selected labels are retained when a page is replaced. For Select, Combobox and Tag combobox, selectedOptions can supply initial records that are not loaded yet. Autocomplete stores the label as free text. Select opens near the selected row when it is loaded; a remote selection on a later page requires an application-owned lookup or seek endpoint. Multi-select opens at the beginning.

Props

Props

NameTypeDefaultDescription
v-modelstring—Current text in the input.
optionsArray<{ value, label, ...meta }> | string[][]Suggestions. May be replaced as query results arrive.
placeholderstring'Search...'Input placeholder.
floatingMode'viewport' | 'anchor''viewport'Choose whether suggestions stay in the browser or follow the input while scrolling.
loadingbooleanfalseShow an inline spinner while async suggestions are being fetched.
filterOptionsbooleantrueSet false for server-filtered suggestions.
hasMorebooleanfalseRequest more through @load-more when scrolling or navigating beyond the loaded page.

Events

  • update:modelValueFired as the text changes.
  • queryFired as the user types. Use this to fetch suggestions.
  • selectFired when a suggestion is chosen. Receives the original item plus value, label, text, and query.
  • commitFired when Enter commits the current text.
  • load-moreRequests the next page. The parent appends options and updates loading/hasMore.