AutoCompleteInput

Search a suggestion list with native text input, keyboard selection and separate query and selected-value state.

AutoCompleteInput

View source ยท API reference

Find a region

Type a label, value or keyword; choose a result with the mouse or arrow keys and Enter. Toggle loading and disabled states to inspect their behavior.

<RegorApp id="auto-complete-input-demo" src="./playground.ts"/>
import {
  type AutoCompleteOption,
  type AutoCompleteValue,
  defineAutoCompleteInputComponents,
  defineButtonComponents,
  defineFlexComponents,
  defineFormComponents,
  defineFormInputField,
  defineIconComponents,
} from '@purestack/ts-components'
import {
  lucide_check,
  lucide_chevron_down,
  lucide_loader_circle,
} from '@purestack/ts-svg-icons'
import { createApp, defineComponent, html, type Ref, ref } from 'regor'

export interface AutoCompleteInputExample {
  searchQuery: Ref<string>
  chosenRegion: Ref<AutoCompleteValue | null>
  regionOptions: AutoCompleteOption[]
  pending: Ref<boolean>
  locked: Ref<boolean>
  reset: () => void
}

const autoCompleteInputExampleTemplate = html`<Flex direction="column">
  <AutoCompleteInput
    id="auto-region"
    label="Deployment region"
    placeholder="Try Europe or Frankfurt"
    :model="searchQuery"
    :selectedValue="chosenRegion"
    :options="regionOptions"
    :loading="pending"
    :disabled="locked"
    emptyText="No matching region"
  />
  <Flex wrap="true">
    <FormCheck id="auto-loading" label="Loading state" :checked="pending" />
    <FormCheck id="auto-disabled" label="Disabled state" :checked="locked" />
    <Btn variant="link" @click="reset">Clear selection</Btn>
  </Flex>
  <FormStatus>
    Query: {{ searchQuery || 'Empty' }} ยท Selected value: {{ chosenRegion ?? 'None'
    }}
  </FormStatus>
</Flex>`

function createAutoCompleteInputExample(): AutoCompleteInputExample {
  const searchQuery = ref('')
  const chosenRegion = ref<AutoCompleteValue | null>(null)
  return {
    searchQuery,
    chosenRegion,
    pending: ref(false),
    locked: ref(false),
    regionOptions: [
      { label: 'Europe Central', value: 'eu-central', keywords: ['Frankfurt'] },
      { label: 'US East', value: 'us-east', keywords: ['Virginia'] },
      { label: 'Asia Pacific', value: 'apac', keywords: ['Singapore'] },
      {
        label: 'Private region ยท unavailable',
        value: 'private',
        disabled: true,
      },
    ],
    reset: () => {
      searchQuery('')
      chosenRegion(null)
    },
  }
}

const component = defineComponent<AutoCompleteInputExample>(
  autoCompleteInputExampleTemplate,
  {
    context: createAutoCompleteInputExample,
  },
)
const icons: Record<string, string> = {
  'lucide:loader-circle': lucide_loader_circle,
  'lucide:check': lucide_check,
  'lucide:chevron-down': lucide_chevron_down,
}

createApp(
  {
    components: {
      AutoCompleteInputExample: component,

      ...defineAutoCompleteInputComponents(),
      ...defineFormInputField(),
      ...defineFlexComponents(),
      ...defineFormComponents(),
      ...defineButtonComponents(),
      ...defineIconComponents((name) => icons[name] ?? ''),
    },
  },
  {
    selector: 'app#auto-complete-input-demo',
    template: html`<AutoCompleteInputExample />`,
  },
)

A custom suggestion row

Rows can show richer context while the parent owns listbox semantics and selection. This example waits for one character and limits results to three.

<RegorApp id="auto-custom-row-demo" src="./custom-row.ts"/>
import {
  type AutoCompleteOption,
  defineAutoCompleteInputComponents,
  defineBadgeComponents,
  defineButtonComponents,
  defineFlexComponents,
  defineFormComponents,
  defineFormInputField,
  defineIconComponents,
  type ResolvedAutoCompleteOption,
} from '@purestack/ts-components'
import { lucide_check, lucide_chevron_down } from '@purestack/ts-svg-icons'
import {
  createApp,
  defineComponent,
  html,
  type Ref,
  type RefOrValue,
  ref,
} from 'regor'

export interface ServiceSuggestion {
  option: RefOrValue<ResolvedAutoCompleteOption | null>
  selected: RefOrValue<boolean>
}
const serviceSuggestionTemplate = html`<Flex align="center" justify="between">
  <span>
    <strong>{{ option?.label }}</strong>
    <small class="d-block">Service ID: {{ option?.value }}</small>
  </span>
  <Badge r-if="selected" tone="success">Selected</Badge>
</Flex>`
const serviceSuggestion = defineComponent<ServiceSuggestion>(
  serviceSuggestionTemplate,
  { props: ['option', 'selected'] },
)
export interface CustomSuggestions {
  serviceQuery: Ref<string>
  serviceOptions: AutoCompleteOption[]
}

const customSuggestionsTemplate = html`<AutoCompleteInput
  id="auto-service"
  label="Service"
  :model="serviceQuery"
  :options="serviceOptions"
  rowComponent="ServiceSuggestion"
  :minLength="1"
  :maxResults="3"
  placeholder="Type API or mail"
/>`

function createCustomSuggestions(): CustomSuggestions {
  return {
    serviceQuery: ref(''),
    serviceOptions: [
      { label: 'Identity API', value: 'identity', keywords: ['auth'] },
      { label: 'Mail gateway', value: 'mail', keywords: ['email'] },
      { label: 'Billing API', value: 'billing', keywords: ['invoice'] },
    ],
  }
}

const component = defineComponent<CustomSuggestions>(
  customSuggestionsTemplate,
  {
    context: createCustomSuggestions,
  },
)
const icons: Record<string, string> = {
  'lucide:check': lucide_check,
  'lucide:chevron-down': lucide_chevron_down,
}

createApp(
  {
    components: {
      CustomSuggestions: component,
      ServiceSuggestion: serviceSuggestion,
      ...defineAutoCompleteInputComponents(),
      ...defineFormInputField(),
      ...defineFlexComponents(),
      ...defineFormComponents(),
      ...defineButtonComponents(),
      ...defineBadgeComponents(),
      ...defineIconComponents((name) => icons[name] ?? ''),
    },
  },
  {
    selector: 'app#auto-custom-row-demo',
    template: html`<CustomSuggestions />`,
  },
)

Behavior and accessibility

Arrow keys move through enabled suggestions; Enter selects, Escape closes. Search matches labels, values and keywords case-insensitively. Editing the query can clear selectedValue. Query text alone is not proof that an option was selected. Keep custom rows free of nested interactive controls.

API reference

AutoCompleteInput contract

Props below are the public template API. RefOrValue accepts a literal or a reactive ref; bind refs with a colon-prefixed attribute. Native attributes and events can be passed through the component root.

id

RefOrValue<string>
Default
generated

Unique control ID. Supply an explicit value when help text or another element needs to reference this control.

label

RefOrValue<string>
Default
not set

Visible label. Use language that describes the control or value in its surrounding context.

tone

RefOrValue<SemanticTone>
Default
inherited

Semantic intent: neutral, accent, secondary, info, success, warning, danger, feature, custom or ghost. The active skin supplies the colors.

variant

RefOrValue<ComponentVariant>
Default
inherited FormInputField surfaceAlt

Visual treatment. Accepts solid, surface, surfaceAlt, spotlight, glass, flat, flatAlt, flatSolid, outlineFill, outline, subtle, subtleBtn, link, sheen, underline, rail, bracket or none.

model

Ref<string>
Default
ref('')

Two-way value ref. Pass the ref itself with :model="fieldValue" so edits update application state.

selectedValue

Ref<AutoCompleteValue | null>
Default
ref(null)

Selected option value, independent of the editable query model.

type

RefOrValue<FormInputFieldType>
Default
search

Native input type forwarded to FormInputField.

name

RefOrValue<string>
Default
not set

Native form field name used during FormData serialization.

required

RefOrValue<boolean>
Default
false

Native required constraint. Validate the owning form before accepting its data.

autocomplete

RefOrValue<string>
Default
off

Native browser autofill hint, such as name, email or current-password.

placeholder

RefOrValue<string>
Default
not set

A short input hint shown while empty. Keep a separate visible label.

disabled

RefOrValue<boolean>
Default
false

Disables interaction. Use a bound boolean, for example :disabled="isLocked".

icon

RefOrValue<string>
Default
not set

Registered SVG icon name. In browser apps, include the icon in the resolver passed to defineIconComponents.

iconEnd

RefOrValue<string>
Default
lucide:chevron-down; loader-circle while loading

Trailing input icon. Include both default icons in the browser resolver when the loading state is used. An explicit iconEnd overrides both.

options

RefOrValue<AutoCompleteOption[]>
Default
[]

Suggestion or select data. Each option has a label, an optional value and an optional disabled flag. Omitted values use the label.

rowComponent

RefOrValue<string>
Default
AutoCompleteOptionRow

Registered suggestion row component. Receives option, item, index, query, active, selected and disabled. The parent supplies the clickable option wrapper.

minLength

RefOrValue<number | string>
Default
0

Minimum trimmed query length before suggestions may open.

maxResults

RefOrValue<number | string>
Default
10

Maximum number of matching suggestions rendered.

openOnFocus

RefOrValue<boolean>
Default
true

Opens suggestions on focus when the minimum query length is met.

loading

RefOrValue<boolean>
Default
false

Shows a loading state. Your application supplies options and manages any asynchronous request.

emptyText

RefOrValue<string>
Default
No suggestions

Message displayed when the query has no matching suggestions.

loadingText

RefOrValue<string>
Default
Loading suggestions

Status message while loading is true.

onSelect

(option: ResolvedAutoCompleteOption) => void
Default
not set

Callback receiving a ResolvedAutoCompleteOption after selection.

onSearch

(query: string) => void
Default
not set

Callback receiving the query string after an edit. Use it to load or filter application data.

Composition

No slots. Register defineAutoCompleteInputComponents, defineFormInputField and defineIconComponents. Custom rows are registered under the name passed to rowComponent.

Option and event contracts

interface AutoCompleteOption {
  label: RefOrValue<string>
  value?: RefOrValue<string | number>
  disabled?: RefOrValue<boolean>
  keywords?: RefOrValue<string | string[]>
}

The component also emits optionselect and querychange. Use callbacks or emitted events for application integration; avoid processing the same change through both paths.