Skip to main content

AutoComplete

A combo-box where the trigger is the input. Type to filter options, use arrow keys to navigate the list, Enter to select, Escape to dismiss, and × to clear. Supports outlined and filled variants, sm / md / lg sizes, option icons, sub-labels, helper text, error states, and async loading skeletons.

Basic

import { html, atom, renderApp } from 'mates';
import { AutoComplete } from 'mates-ui';

const COUNTRIES = [
{ id: "us", label: "United States", subLabel: "North America" },
{ id: "uk", label: "United Kingdom", subLabel: "Europe" },
{ id: "de", label: "Germany", subLabel: "Europe" },
{ id: "fr", label: "France", subLabel: "Europe" },
{ id: "jp", label: "Japan", subLabel: "Asia" },
{ id: "au", label: "Australia", subLabel: "Oceania" },
];

const App = () => {
const selected = atom(null);
return () => html`
<x-col gap="var(--md-space-4)" style="max-width:22rem;">
${AutoComplete({
options: COUNTRIES,
value: selected(),
label: "Country",
placeholder: "Search countries…",
onChange: (id) => selected.set(id),
})}
</x-col>
`;
};

renderApp(App, document.getElementById('app'));

Variants & Sizes

import { html, atom, renderApp } from 'mates';
import { AutoComplete } from 'mates-ui';

const OPTIONS = [
{ id: "us", label: "United States", subLabel: "North America" },
{ id: "uk", label: "United Kingdom", subLabel: "Europe" },
{ id: "de", label: "Germany", subLabel: "Europe" },
{ id: "fr", label: "France", subLabel: "Europe" },
{ id: "jp", label: "Japan", subLabel: "Asia" },
];

const App = () => {
const v1 = atom(null); const v2 = atom(null); const v3 = atom(null);
const v4 = atom(null); const v5 = atom(null); const v6 = atom(null);
return () => html`
<x-row gap="var(--md-space-6)" style="max-width:48rem;align-items:flex-start;">
<x-col gap="var(--md-space-3)" style="flex:1;">
${AutoComplete({ options: OPTIONS, value: v1(), label: "Outlined sm", variant: "outlined", size: "sm", onChange: (id) => v1.set(id) })}
${AutoComplete({ options: OPTIONS, value: v2(), label: "Outlined md", variant: "outlined", size: "md", onChange: (id) => v2.set(id) })}
${AutoComplete({ options: OPTIONS, value: v3(), label: "Outlined lg", variant: "outlined", size: "lg", onChange: (id) => v3.set(id) })}
</x-col>
<x-col gap="var(--md-space-3)" style="flex:1;">
${AutoComplete({ options: OPTIONS, value: v4(), label: "Filled sm", variant: "filled", size: "sm", onChange: (id) => v4.set(id) })}
${AutoComplete({ options: OPTIONS, value: v5(), label: "Filled md", variant: "filled", size: "md", onChange: (id) => v5.set(id) })}
${AutoComplete({ options: OPTIONS, value: v6(), label: "Filled lg", variant: "filled", size: "lg", onChange: (id) => v6.set(id) })}
</x-col>
</x-row>
`;
};

renderApp(App, document.getElementById('app'));

States — Helper, Error, Disabled, Loading

import { html, atom, renderApp } from 'mates';
import { AutoComplete } from 'mates-ui';

const OPTIONS = [
{ id: "us", label: "United States", subLabel: "North America" },
{ id: "uk", label: "United Kingdom", subLabel: "Europe" },
{ id: "de", label: "Germany", subLabel: "Europe" },
];

const App = () => {
const v1 = atom(null); const v2 = atom(null);
return () => html`
<x-col gap="var(--md-space-5)" style="max-width:28rem;">
${AutoComplete({ options: OPTIONS, value: v1(), label: "With helper text", placeholder: "Search…", helper: "Start typing to filter.", onChange: (id) => v1.set(id) })}
${AutoComplete({ options: OPTIONS, value: v2(), label: "With error", placeholder: "Search…", error: "Please select a valid country.", onChange: (id) => v2.set(id) })}
${AutoComplete({ options: OPTIONS, value: "de", label: "Disabled", placeholder: "Search…", disabled: true, onChange: () => {} })}
${AutoComplete({ options: [], value: null, label: "Loading", placeholder: "Loading options…", loading: true, skeletonCount: 3, onChange: () => {} })}
</x-col>
`;
};

renderApp(App, document.getElementById('app'));

Multiple selection

import { html, atom, renderApp } from 'mates';
import { AutoComplete } from 'mates-ui';

const OPTIONS = [
{ id: "us", label: "United States", subLabel: "North America" },
{ id: "uk", label: "United Kingdom", subLabel: "Europe" },
{ id: "de", label: "Germany", subLabel: "Europe" },
{ id: "fr", label: "France", subLabel: "Europe" },
{ id: "jp", label: "Japan", subLabel: "Asia" },
];

const App = () => {
const selected = atom([]);
return () => html`
<x-col gap="var(--md-space-4)" style="max-width:28rem;">
${AutoComplete({
options: OPTIONS,
multiple: true,
values: selected(),
label: "Countries",
placeholder: "Search and pick countries…",
onChangeMultiple: (ids) => selected.set(ids),
})}
</x-col>
`;
};

renderApp(App, document.getElementById('app'));

Options with Icons

import { html, atom, renderApp } from 'mates';
import { AutoComplete } from 'mates-ui';
import { AdminPanelSettingsIcon, EditIcon, VisibilityIcon, StarIcon } from 'mates-icons';

const ROLES = [
{ id: "admin", label: "Admin", subLabel: "Full access", icon: AdminPanelSettingsIcon() },
{ id: "editor", label: "Editor", subLabel: "Can edit content", icon: EditIcon() },
{ id: "viewer", label: "Viewer", subLabel: "Read only", icon: VisibilityIcon() },
{ id: "owner", label: "Owner", subLabel: "Ownership rights", icon: StarIcon() },
];

const App = () => {
const role = atom(null);
return () => html`
<x-col gap="var(--md-space-4)" style="max-width:22rem;">
${AutoComplete({
options: ROLES,
value: role(),
label: "Assign role",
placeholder: "Search roles…",
onChange: (id) => role.set(id),
})}
</x-col>
`;
};

renderApp(App, document.getElementById('app'));

AutoComplete

import { AutoComplete } from "mates-ui"; import type { AutoCompleteOption } from "mates-ui";
Prop Type Default Description
attr AttrMap HTML attributes on the root element.
classes string | string[] Extra root classes — joined and appended. Joined and appended after built-in classes.
clearAllLabel string "Clear all" Clear-all button aria-label (multiple mode).
clearLabel string "Clear" Clear button aria-label (single mode).
data DataAttrMap Root `data-*` map (`testId` → `data-testid`). `testId` prop / `attr` win over `data`.
disabled boolean false Disables the field and all interaction.
emptyText string "No options" Text shown when no options match the typed query.
error string Error message shown below the field in red; also applies error border styling.
helper string Helper text shown below the field in muted colour.
id string Sets the id attribute on the root element.
label string Label rendered above the input field.
loading boolean false Replaces list items with skeleton rows.
maxHeight string "17.5rem" Max height of the dropdown list scroll area.
maxVisibleTags number 3 Maximum chips shown in the trigger before a +N overflow badge appears. Only used when multiple=true.
multiple boolean false Enable multi-select mode. Use values / onChangeMultiple instead of value / onChange.
on OnEventMap DOM event handlers on the root element.
onChange (value: string | null, option: AutoCompleteOption | null) => void Called on selection or clear. Both args are null when the field is cleared.
onChangeMultiple (values: string[], options: AutoCompleteOption[]) => void Fires when the multi-selection changes. Receives the full new IDs array and the matching option objects.
options* AutoCompleteOption[] List of options to display and filter.
placeholder string "Search…" Input placeholder text shown when nothing is typed.
popupMinWidth string CSS min-width of the floating panel. Defaults to matching the input width.
removeOptionLabel string "Remove {label}" Remove-chip aria-label template; `{label}` is replaced with the option label.
size "sm" | "md" | "lg" "md" Controls the field height and font size.
skeletonCount number Number of skeleton rows rendered when loading is true.
style StyleMap Inline CSS on the root element.
testId string Overrides the default data-testid attribute on the root element.
value string | null Currently selected option id. Pass null for no selection.
values string[] Controlled array of selected option IDs (multiple mode). Each selected item appears as a removable chip in the trigger.
variant "outlined" | "filled" "outlined" Visual style of the input field border.