AutocompleteSelect
Generic searchable async select with Popover, debounced search, keyboard navigation, and custom item rendering.
Overview
The AutocompleteSelect component provides a searchable dropdown that
fetches options asynchronously. It's generic over the item type T, supports debounced search, keyboard navigation (arrow keys, Enter,
Escape), custom item rendering, and composes Popover internally.
Use it for patient lookups, entity searches, and any scenario requiring async
search-and-select.
import { AutocompleteSelect } from '@enara-health/ui-react';
<AutocompleteSelect
value={selectedId}
onChange={setSelectedId}
fetchItems={searchPatients}
getItemKey={(p) => p.id}
getItemLabel={(p) => p.name}
placeholder="Search patients..."
/>
The matching logic lives in your fetchItems function — the component
is agnostic to how you filter. Here are three common strategies:
Starts with
Alphabetical filtering — try "bl"
Contains
Match anywhere in the label — try "an"
Fuzzy
Characters in order — try "bry"
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | null | - | Required. Currently selected item key |
onChange | (value: string | null) => void | - | Required. Callback when selection changes |
fetchItems | (searchTerm: string) => Promise<T[]> | - | Required. Async function to fetch items based on search term |
getItemKey | (item: T) => string | - | Required. Extracts the unique key from an item |
getItemLabel | (item: T) => string | - | Required. Extracts the display label from an item |
placeholder | string | 'Select item' | Placeholder text when no item is selected |
renderItem | (item: T) => ReactNode | - | Custom render function for dropdown items |
initialDisplayText | string | '' | Pre-populated display text when a value is already selected on mount |
emptyMessage | string | 'No items found' | Message shown when search returns no results |
disabled | boolean | false | Disables the component and prevents interaction |
className | string | - | Additional CSS class names |
Variants
Basic Async Search
The default usage with an async data source.
const searchPatients = async (term: string) => {
const res = await fetch(`/api/patients?q=${term}`);
return res.json();
};
<AutocompleteSelect
value={selectedId}
onChange={setSelectedId}
fetchItems={searchPatients}
getItemKey={(p) => p.id}
getItemLabel={(p) => p.name}
placeholder="Search patients..."
/> Custom Item Rendering
Use renderItem to display rich content in the dropdown options.
<AutocompleteSelect
value={selectedId}
onChange={setSelectedId}
fetchItems={searchUsers}
getItemKey={(u) => u.id}
getItemLabel={(u) => u.name}
renderItem={(u) => (
<Box display="flex" gap={8} alignItems="center">
<Avatar src={u.avatar} size="sm" />
<div>
<Text weight="medium">{u.name}</Text>
<Text size="xs" color="muted">{u.email}</Text>
</div>
</Box>
)}
/> Pre-Selected Value
Use initialDisplayText when a value is already selected on mount
(e.g., editing an existing record).
<AutocompleteSelect
value={patient.practitionerId}
onChange={handlePractitionerChange}
fetchItems={searchPractitioners}
getItemKey={(p) => p.id}
getItemLabel={(p) => p.name}
initialDisplayText={patient.practitionerName}
placeholder="Assign practitioner..."
/> Disabled State
Prevent interaction when the field should be read-only.
<AutocompleteSelect
value={selectedId}
onChange={setSelectedId}
fetchItems={searchItems}
getItemKey={(item) => item.id}
getItemLabel={(item) => item.name}
disabled
/> Examples
Patient Lookup
Search and select a patient from the database.
const searchPatients = async (term: string) => {
const response = await api.get('/patients', { params: { search: term } });
return response.data;
};
<FormField label="Patient">
<AutocompleteSelect
value={patientId}
onChange={setPatientId}
fetchItems={searchPatients}
getItemKey={(p) => p.id}
getItemLabel={(p) => `${p.firstName} ${p.lastName}`}
placeholder="Search by name or ID..."
emptyMessage="No patients found"
/>
</FormField> In a Form with FormField
Combine with FormField for label and error state support.
<FormField label="Assigned Practitioner" error={errors.practitioner}>
<AutocompleteSelect
value={formData.practitionerId}
onChange={(id) => setFormData(prev => ({ ...prev, practitionerId: id }))}
fetchItems={searchPractitioners}
getItemKey={(p) => p.id}
getItemLabel={(p) => p.name}
initialDisplayText={formData.practitionerName}
/>
</FormField> Accessibility
- Pattern: Implements the combobox pattern with search input and listbox.
- Keyboard Support: Arrow Up/Down to navigate items, Enter to select, Escape to close, typing triggers search.
- Focus Management: Focus moves to the search input when the dropdown opens.
- Screen Reader: Trigger reads as a button with the current selected value or placeholder text.
- WCAG Compliance: Meets AA standards for interactive form controls.
Best Practices
-
Use for async data sources — for small static lists, use
Selectinstead -
Provide meaningful
placeholdertext that describes what to search for -
Customize
emptyMessageto guide users when no results are found -
Wrap in
FormFieldfor label, description, and error support -
Use
renderItemto show additional context (avatar, email, etc.)