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.