Skip to content

TkxAutocomplete

Type-ahead with filtering

Selected: (none)

import { TkxAutocomplete } from 'tekivex-ui';
<TkxAutocomplete
label="City"
options={[
{ value: 'tokyo', label: 'Tokyo' },
{ value: 'osaka', label: 'Osaka' },
{ value: 'kyoto', label: 'Kyoto' },
]}
onChange={(value) => console.log(value)}
/>
freeSolo (accept any input)

Value: (none)

<TkxAutocomplete
label="Tag"
options={tagOptions}
freeSolo
onChange={(v) => /* may be one of options OR a typed string */}
/>

Without freeSolo, the input rejects values that don’t match an option.

const [options, setOptions] = useState([]);
const [loading, setLoading] = useState(false);
<TkxAutocomplete
label="Search users"
options={options}
isLoading={loading}
onInputChange={async (q) => {
if (q.length < 2) return;
setLoading(true);
const res = await fetch(`/api/users?q=${q}`).then(r => r.json());
setOptions(res);
setLoading(false);
}}
/>
<TkxAutocomplete
options={users}
filterFn={(opt, query) => {
// Match on label OR email
const q = query.toLowerCase();
return opt.label.toLowerCase().includes(q) || opt.email.includes(q);
}}
/>
  • ARIA combobox + listbox pattern (WAI-ARIA 1.2)
  • Input has role="combobox" with aria-expanded, aria-controls, aria-activedescendant
  • Options have role="option" + aria-selected
  • Keyboard: arrows navigate, Enter selects, Escape closes, Tab accepts current selection (free-solo only)
  • The empty state message is localised via the active locale
  • Loading state announces via aria-busy
PropTypeRequiredDescription
optionsAutocompleteOption[]yes{ value, label }.
valuestringControlled value.
onChange(value: string) => voidSelected option change.
onInputChange(query: string) => voidFires on every keystroke (use for async).
placeholderstring
labelstringyesWCAG-required label.
isLoadingbooleanShow loading state.
emptyMessagestringLocalised by default.
filterFn(opt, query) => booleanCustom matcher.
freeSolobooleanAccept arbitrary text.

src/components/TkxAutocomplete.tsx