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.
- Ada Lovelace
- Grace Hopper
- Katherine Johnson
Properties
Control props
Current text value.
Placeholder shown when the input is empty.
Suggestions. May be replaced by async lookup results.
Preferred side before collision handling.
viewport keeps suggestions inside the browser; anchor keeps them attached while scrolling.
Field props
Optional ID override. By default parent forms derive the input ID from the field path using underscores.
Local field name. Parent forms derive the full field path and native HTML name from the form hierarchy.
Visible field label.
Optional helper copy below the field.
Validation errors for this field.
Validators
No validators yet.
Validators attached to this field. Use functions in Vue code, or serializable records such as { name: "minLength", props: { min: 2 } } in generated schemas.
Render default field chrome, or hide chrome while keeping form state wiring.
npm install @getdom/studio<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.
- Ada Lovelace
- Grace Hopper
- Katherine Johnson
Typed text: —
Selected item: —
npm install @getdom/studio<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.
- No options found
Browse paginated suggestions or commit your own text.
0 of 0 results loaded. Scroll for more; search resets the pages.
Value: None
npm install @getdom/studio<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 autocompleteBehaviour
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
| Name | Type | Default | Description |
|---|---|---|---|
| v-model | string | — | Current text in the input. |
| options | Array<{ value, label, ...meta }> | string[] | [] | Suggestions. May be replaced as query results arrive. |
| placeholder | string | 'Search...' | Input placeholder. |
| floatingMode | 'viewport' | 'anchor' | 'viewport' | Choose whether suggestions stay in the browser or follow the input while scrolling. |
| loading | boolean | false | Show an inline spinner while async suggestions are being fetched. |
| filterOptions | boolean | true | Set false for server-filtered suggestions. |
| hasMore | boolean | false | Request 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.