Skip to content
ShineUI
Install

AutoComplete

Suggest as they type.

<sui-auto-complete>SelectionStable

About

Suggest-as-you-type text field. The typed substring renders in bold in every match. By default it filters options itself; a host that fetches sets externalFilter and answers suiSearch. With multiple each pick becomes a removable chip before the query. Form-associated: posts the typed text, or one entry per value.

Usage

<sui-auto-complete label="Customer" name="customer" placeholder="Start typing a name" help="Type to search 143 customers."></sui-auto-complete> <script type="module"> import { defineCustomElements } from "@shineui/core/loader"; import "@shineui/core/shineui.css"; defineCustomElements(); const el = document.querySelector("sui-auto-complete"); el.options = ["Clark County Schools", "Derby City Events", "Riverbend Dental Group", "Silver Hills Church"]; /* SuiAutoCompleteEntry[] */ el.addEventListener("suiChange", (e) => console.log(e.detail)); </script>
import { SuiAutoComplete } from "@shineui/react"; import "@shineui/core/shineui.css"; export function Example() { const options = ["Clark County Schools", "Derby City Events", "Riverbend Dental Group", "Silver Hills Church"]; /* SuiAutoCompleteEntry[] */ return <SuiAutoComplete label="Customer" name="customer" placeholder="Start typing a name" help="Type to search 143 customers." options={options} onSuiChange={(e) => console.log(e.detail)} />; }
<script setup> import { SuiAutoComplete } from "@shineui/vue"; import "@shineui/core/shineui.css"; const options = ["Clark County Schools", "Derby City Events", "Riverbend Dental Group", "Silver Hills Church"]; // SuiAutoCompleteEntry[] const onChange = (e) => console.log(e.detail); </script> <template> <SuiAutoComplete label="Customer" name="customer" placeholder="Start typing a name" help="Type to search 143 customers." :options="options" @sui-change="onChange" /> </template>
import { Component } from "@angular/core"; import { SuiAutoComplete } from "@shineui/angular"; @Component({ standalone: true, imports: [SuiAutoComplete], template: `<sui-auto-complete label="Customer" name="customer" placeholder="Start typing a name" help="Type to search 143 customers." [(open)]="open" [options]="options" (suiChange)="onChange($event)"></sui-auto-complete>`, }) export class Example { open = false; options = ["Clark County Schools", "Derby City Events", "Riverbend Dental Group", "Silver Hills Church"]; // SuiAutoCompleteEntry[] onChange(e: CustomEvent) { console.log(e.detail); } }

Basic

The field is a combobox with aria-autocomplete="list" that points at the active suggestion through aria-activedescendant. The panel opens only once there is text to suggest against, on typing or ArrowDown.

<div style="max-width:340px"> <sui-auto-complete label="Customer" name="customer" placeholder="Start typing a name" help="Type to search 143 customers."></sui-auto-complete> </div>
// setup(root): properties and events setup(root) { root.querySelector('sui-auto-complete').options = customers; }

Matches and no matches

The typed substring renders in bold. When nothing matches, emptyFilterMessage takes the panel and says No matches. A panel opened over no suggestions at all shows emptyMessage, which says Nothing here yet: an empty list and a search that found nothing are two states, and one sentence cannot be right about both.

<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:start;min-height:240px"> <sui-auto-complete id="ac-hit" label="Customer" value="riv" open></sui-auto-complete> <sui-auto-complete id="ac-miss" label="Customer" value="zzz" open empty-message="No customer matches that name."></sui-auto-complete> </div>
// setup(root): properties and events setup(root) { root.querySelector('#ac-hit').options = customers; root.querySelector('#ac-miss').options = customers; }

Dropdown button

dropdown draws a chevron button after the text that opens the panel with every option, no typing needed, and fires suiSearch with the reason dropdown so a host that fetches can answer with the full list. clearable draws a Clear button beside it; both are real buttons, so Tab reaches them.

<div style="max-width:340px;min-height:260px"> <sui-auto-complete id="ac-drop" label="Customer" placeholder="Type, or open the list" dropdown clearable></sui-auto-complete> </div>
// setup(root): properties and events setup(root) { root.querySelector('#ac-drop').options = customers; }

Opening on focus

A field that already holds text opens nothing when it takes focus, so tabbing through a form never drops a list over the next field; typing, ArrowDown or the dropdown button open it. open-on-focus, on the second field, opens it on focus too, for a field people pick from again.

<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:start;min-height:150px"> <sui-auto-complete id="ac-focus-off" label="Customer" value="River"></sui-auto-complete> <sui-auto-complete id="ac-focus-on" label="Recent search" value="River" open-on-focus></sui-auto-complete> </div>
// setup(root): properties and events setup(root) { root.querySelector('#ac-focus-off').options = customers; root.querySelector('#ac-focus-on').options = customers; }

Object and grouped options

An option object is { label, value, disabled }: the field shows and posts the label, and suiSelect reports the value, so a host can keep the id. A group is { label, items } and draws an eyebrow header over the rows that match.

Pick a row to see its id.
<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:start;min-height:280px"> <div style="display:grid;gap:8px"> <sui-auto-complete id="ac-objects" label="Customer" value="river" dropdown open></sui-auto-complete> <span id="ac-objects-log" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Pick a row to see its id.</span> </div> <sui-auto-complete id="ac-groups" label="Contact" value="derby" open></sui-auto-complete> </div>
// setup(root): properties and events setup(root) { const objects = root.querySelector('#ac-objects'); const log = root.querySelector('#ac-objects-log'); objects.options = customerRecords; objects.addEventListener('suiSelect', (e) => { log.textContent = `suiSelect ${e.detail}, the field shows ${objects.value}.`; }); root.querySelector('#ac-groups').options = groupedContacts; }

Several values

multiple turns each pick into a chip, and value becomes the list of picked values: the chip shows the name, the form posts the address. A pick empties the query and keeps the list open; a checked row picked again leaves, as Backspace in an empty query does. allow-custom on Cc takes a typed address; max caps it at five.

Submit to see what posts.
<form id="ac-multi-form" style="display:grid;gap:14px;max-width:420px"> <sui-auto-complete id="ac-multi-to" label="To" name="to" multiple required clearable placeholder="Add people from the directory"></sui-auto-complete> <sui-auto-complete id="ac-multi-cc" label="Cc" name="cc" multiple allow-custom max="5" placeholder="Add people or type an address" help="Up to five. Press Enter after an address that is not in the directory."></sui-auto-complete> <sui-button type="submit" variant="brand" label="Send"></sui-button> <output id="ac-multi-out" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Submit to see what posts.</output> </form>
// setup(root): properties and events setup(root) { const form = root.querySelector('#ac-multi-form'); const out = root.querySelector('#ac-multi-out'); const to = root.querySelector('#ac-multi-to'); to.options = staff; to.value = ['dana@fallscitysupply.com']; root.querySelector('#ac-multi-cc').options = staff; form.addEventListener('submit', (e) => { e.preventDefault(); const entries = [...new FormData(form)].map(([k, v]) => `${k}=${v}`); out.textContent = entries.length ? entries.join(' ') : 'Nothing posted.'; }); }

As a property

variant="quiet" draws the text as it is in the surrounding font, with no shell at rest: the fill and the Clear and Show options buttons come on hover, and the shell while the field has focus or the panel is open, since a focused text field is being typed into. The label is not drawn but still names the field.

Customer Vendor
<div style="display:grid;grid-template-columns:76px minmax(0,1fr);gap:12px 10px;align-items:center;max-width:320px;font:550 13.5px/1.5 var(--font-sans);color:var(--ink)"> <span style="font:460 12.5px/1.3 var(--font-sans);color:var(--ink-muted)">Customer</span><sui-auto-complete id="ac-prop-customer" variant="quiet" label="Customer" size="sm" value="Riverbend Dental Group" dropdown clearable></sui-auto-complete> <span style="font:460 12.5px/1.3 var(--font-sans);color:var(--ink-muted)">Vendor</span><sui-auto-complete id="ac-prop-vendor" variant="quiet" label="Vendor" size="sm" placeholder="None" dropdown></sui-auto-complete> </div>
// setup(root): properties and events setup(root) { root.querySelector('#ac-prop-customer').options = customers; root.querySelector('#ac-prop-vendor').options = ['Derby Paper Supply', 'Riverbend Office Cleaning']; }

Force selection

forceSelection lets only a suggestion stand: when the text commits, a label match (ignoring case) is picked as that suggestion and anything else is wiped. The wipe is an edit the element made, so suiInput reports the empty text and suiChange commits it. Type a name that is not on the list and leave the field.

Nothing committed yet.
<div style="display:flex;flex-wrap:wrap;gap:14px;align-items:flex-end"> <div style="max-width:300px;flex:1 1 240px"><sui-auto-complete id="ac-force" label="Customer" placeholder="Must be an existing customer" force-selection dropdown></sui-auto-complete></div> <span id="ac-force-log" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Nothing committed yet.</span> </div>
// setup(root): properties and events setup(root) { const el = root.querySelector('#ac-force'); const log = root.querySelector('#ac-force-log'); el.options = customers; el.addEventListener('suiInput', (e) => { if (!e.detail && el.value === '') log.textContent = 'Wiped: not a customer.'; }); el.addEventListener('suiChange', (e) => { log.textContent = e.detail ? `Committed ${e.detail}.` : 'Wiped: not a customer.'; }); }

Loading and sizes

While loading is set a spinner sits at the end of the field and the panel shows the loading row instead of rows. size is the same sm, md, lg vocabulary and the same 28, 34, 42 heights as the rest of the control family.

<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:end"> <sui-auto-complete id="ac-loading" label="Customer" value="riv" placeholder="Start typing a name" loading dropdown></sui-auto-complete> <sui-auto-complete id="ac-sm" label="Small" size="sm" placeholder="Start typing a name"></sui-auto-complete> <sui-auto-complete id="ac-lg" label="Large" size="lg" placeholder="Start typing a name" clearable value="Silver Hills Church"></sui-auto-complete> </div>
// setup(root): properties and events setup(root) { ['#ac-loading', '#ac-sm', '#ac-lg'].forEach((id) => { root.querySelector(id).options = customers; }); }

States

An error replaces the help line, paints the danger border and sets aria-invalid; invalid is the border alone, for a value the host checks on save. A disabled field posts nothing and never holds the form.

<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:start"> <sui-auto-complete id="ac-req" label="Customer" required placeholder="Start typing a name"></sui-auto-complete> <sui-auto-complete id="ac-err" label="Customer" required error="Customer is required."></sui-auto-complete> <sui-auto-complete id="ac-off" label="Customer" value="Silver Hills Church" disabled help="Set when the invoice was issued."></sui-auto-complete> <sui-auto-complete id="ac-invalid" label="Customer" value="Unknown name" invalid help="Checked against the customer list on save."></sui-auto-complete> </div>
// setup(root): properties and events setup(root) { ['#ac-req', '#ac-err', '#ac-off', '#ac-invalid'].forEach((id) => { root.querySelector(id).options = customers; }); }

Remote suggestions

Set external-filter when the host answers suiSearch with the server's own result for that query, because filtering a fuzzy remote answer again on the client would drop rows the server returned. Try typing dentist.

Waiting for a query.
<div style="display:flex;flex-wrap:wrap;gap:14px;align-items:flex-start"> <div style="max-width:300px;flex:1 1 260px"><sui-auto-complete id="ac-remote" label="Customer" external-filter dropdown placeholder="Try dentist or catering"></sui-auto-complete></div> <span id="ac-remote-log" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Waiting for a query.</span> </div>
// setup(root): properties and events setup(root) { const el = root.querySelector('#ac-remote'); const log = root.querySelector('#ac-remote-log'); el.addEventListener('suiSearch', (e) => { const { query, reason } = e.detail; const q = reason === 'dropdown' ? '' : query.trim().toLowerCase(); const hits = reason === 'dropdown' ? directory : q ? directory.filter((c) => c.name.toLowerCase().includes(q) || c.terms.some((t) => t.startsWith(q))) : []; el.options = hits.map((c) => c.name); log.textContent = reason === 'dropdown' ? `Server returned all ${hits.length} rows.` : q ? `Server returned ${hits.length} rows for ${q}.` : 'Waiting for a query.'; }); }

A long list

From virtualScrollThreshold suggestions, 200 by default, the panel draws only the rows near the view and stands the rest in a blank the height of the list. The Show options button browses all five thousand; the arrows and the position a screen reader reads still count over every one.

Press the chevron to browse all of them.
<div style="display:flex;flex-wrap:wrap;gap:14px;align-items:flex-start;min-height:320px"> <div style="max-width:340px;flex:1 1 280px"><sui-auto-complete id="ac-long" label="Customer" placeholder="Start typing a name" dropdown clearable></sui-auto-complete></div> <span id="ac-long-log" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Press the chevron to browse all of them.</span> </div>
// setup(root): properties and events setup(root) { const el = root.querySelector('#ac-long'); const log = root.querySelector('#ac-long-log'); el.options = longCustomers(5000); const count = () => { // The open panel renders inside the element. const panel = el; log.textContent = `5,000 suggestions, ${panel.querySelectorAll('[role="option"]').length} rows in the page.`; }; el.addEventListener('suiOpen', () => requestAnimationFrame(count)); el.addEventListener('suiSelect', (e) => { log.textContent = `Picked ${e.detail}.`; }); }

Read-only

readonly forwards to the native input, opens nothing, keeps the dropdown and clear controls off, and the text still posts.

<div style="display:grid;gap:14px;max-width:320px"> <sui-auto-complete id="ro-sui-auto-complete" label="Customer" name="sui-auto-complete" value="Derby City Events" readonly required dropdown clearable></sui-auto-complete> </div>
// setup(root): properties and events setup(root) { root.querySelector('#ro-sui-auto-complete').options = customers; }

Empty list

emptyMessage is the row for a suggestion list with nothing in it, as when a fetch has not answered yet; emptyFilterMessage is the row for typed text that matches nothing, which the Matches and no matches example shows.

<div style="max-width:280px"><sui-auto-complete id="ac-empty" label="Customer" dropdown empty-message="No customers on this workspace yet." open></sui-auto-complete></div>
// setup(root): properties and events setup(root) { root.querySelector('#ac-empty').options = []; }

In a form

The typed text posts under the host name through ElementInternals. An emptied field posts an empty string rather than dropping the key.

Submit to see what posts.
<form id="ac-form" style="display:grid;gap:14px;max-width:340px"> <sui-auto-complete id="ac-form-customer" label="Customer" name="customer" value="Riverbend Dental Group" required></sui-auto-complete> <sui-input-text label="Invoice number" name="invoice" value="INV-1043"></sui-input-text> <sui-button type="submit" variant="brand" label="Create invoice"></sui-button> <output id="ac-out" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Submit to see what posts.</output> </form>
// setup(root): properties and events setup(root) { const form = root.querySelector('#ac-form'); const out = root.querySelector('#ac-out'); root.querySelector('#ac-form-customer').options = customers; form.addEventListener('submit', (e) => { e.preventDefault(); const entries = [...new FormData(form)].map(([k, v]) => `${k}=${v}`); out.textContent = entries.length ? entries.join(' ') : 'Nothing posted.'; }); }

Events

suiInput fires on every change of the text, a keystroke or a pick that writes the label, suiSelect when a suggestion is picked, and suiChange when the text commits. suiOpen fires after the element opens the panel, and the cancelable suiClose fires before it closes with a reason of pick, escape, outside or cancel.

Idle.
<div style="display:flex;flex-wrap:wrap;gap:14px;align-items:flex-start"> <div style="max-width:300px;flex:1 1 260px"><sui-auto-complete id="ac-ev" label="Customer" placeholder="Start typing a name"></sui-auto-complete></div> <span id="ac-ev-log" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Idle.</span> </div>
// setup(root): properties and events setup(root) { const el = root.querySelector('#ac-ev'); const log = root.querySelector('#ac-ev-log'); el.options = customers; el.addEventListener('suiInput', (e) => { log.textContent = `Typing ${e.detail || '(empty)'}.`; }); el.addEventListener('suiOpen', () => { log.textContent = 'Panel open.'; }); el.addEventListener('suiSelect', (e) => { log.textContent = `Selected ${e.detail}.`; }); el.addEventListener('suiChange', (e) => { log.textContent = `Committed ${e.detail || '(empty)'}.`; }); el.addEventListener('suiClose', (e) => { log.textContent = `Panel closing (${e.detail}).`; }); el.addEventListener('suiFocus', () => { log.textContent = 'suiFocus'; }); el.addEventListener('suiBlur', () => { log.textContent = 'suiBlur'; }); }

Keyboard

KeyAction
ArrowDown ArrowUpOpens the panel when there is text to suggest against, or with dropdown set, opens every option from an empty field. Once open, moves the active suggestion, skipping disabled ones and wrapping at either end.
EnterPicks the active suggestion. With none active, commits the typed text and closes the panel; with forceSelection set, the text must match a suggestion or it is wiped. In a multiple field it adds the active suggestion, or removes it when it is already picked, and keeps the panel open; with none active, text that spells a suggestion is picked, and other text is added only under allowCustom.
BackspaceIn a multiple field with an empty query, removes the last chip.
EscapeCloses the panel and leaves the text as typed.
TabCloses the panel, then reaches the Clear and Show options buttons when they are drawn, before moving on. Shift+Tab from a multiple field reaches the remove button on each chip.

API

<sui-auto-complete>

Properties

PropertyAttributeTypeDefaultDescription
allowCustomallow-custombooleanfalseMultiple field only: typed text that matches no suggestion can be added as a value of its own, with Enter or by leaving the field. Off by default, so every value comes from the suggestions and a stray Enter adds nothing. Text that equals a suggestion's label, ignoring case, is always picked as that suggestion; text left over that nothing took is cleared when the field loses focus.
clearableclearablebooleanfalseX button after the text that empties the field and commits the empty value. On a multiple field it removes every chip and the query.
disableddisabledbooleanfalseDisables the field. A disabled field posts nothing and never holds the form.
dropdowndropdownbooleanfalseChevron button after the text that opens the panel with every option, no typing needed. It fires suiSearch with the reason dropdown, so a host that fetches can answer with the full list.
emptyFilterMessageempty-filter-messagestringstring | undefinedRow shown when the filter matched nothing. An empty list and a search that found nothing are different states and one sentence cannot be right about both: "Nothing here yet." is wrong over a list of two hundred the reader has just narrowed to none.
emptyMessageempty-messagestringstring | undefinedRow shown when the suggestion list is empty.
errorerrorstringstring | undefinedError message under the field; paints the danger border and sets aria-invalid.
externalFilterexternal-filterbooleanfalseThe host filters: the panel shows options as given, with no match test of its own. Set it when the host answers suiSearch with the server's own answer for that query: a remote result is often a fuzzy or semantic match, and filtering it again on the client would drop rows the server returned. Off by default, so the field filters options by the typed text.
forceSelectionforce-selectionbooleanfalseSingle field only: only a suggestion may stand. When the text commits (Enter with no row active, or leaving the field), text that equals a suggestion's label, ignoring case, is picked as that suggestion; anything else is wiped and an empty value commits. The wipe is an edit the element made, so suiInput reports the empty text before suiChange commits it. A multiple field takes only suggestions unless allowCustom is set.
helphelpstringstring | undefinedHelp text under the field. Replaced by error when set.
invalidinvalidbooleanfalseDanger border without a message, for a value the host checks against its own list on save.
labellabelstringstring | undefinedLabel above the field. Without one the field takes the host's aria-label or aria-labelledby as its name; a visible label wins over both.
loadingloadingbooleanfalseShows the suggestions are still on their way: a spinner sits at the end of the field and the panel shows the loading row instead of rows. Set it while fetching, clear it when options land.
loadingMessageloading-messagestringstring | undefinedText of the loading row.
maxmaxnumbernumber | undefinedMultiple field only: the most values it takes. At the limit the suggestions not yet picked are disabled and a screen reader hears that the limit is reached. A fractional value is floored; anything below 1, or none, means no limit.
multiplemultiplebooleanfalsePicks several values instead of one. value is then an array of option values, each drawn as a removable chip before the query box. A pick adds its value, empties the query and leaves the panel open for the next one; a picked suggestion carries a check, and picking it again removes it. Backspace in an empty query removes the last chip.
namenamestringstring | undefinedForm control name; a multiple field posts one entry per value under it. Reflected: the form entry is built from the content attribute.
openopenbooleanfalseOpens the suggestion panel. Mutable and the single source of truth: while it is true the panel is rendered and aria-expanded is 'true'. The element opens itself only when there is text to suggest against, or from the dropdown button, announces that with suiOpen, and asks with the cancelable suiClose before it closes itself on pick, outside click, Escape, Tab, an emptied field and a form reset. A multiple field stays open on a pick and on an emptied query, and with an empty query it lists every suggestion.
openOnFocusopen-on-focusbooleanfalseOpens the suggestions when the field takes focus while it holds text. Off by default: tabbing through a form shows only the field, so a list never covers the next one, and the suggestions open on typing, ArrowDown or the dropdown button. Set it for a field whose job is to be picked from again, a recent search say. A multiple field, whose query is empty when focus arrives, opens with every suggestion.
optionsproperty onlySuiAutoCompleteEntry[][]Suggestion pool: strings, { label, value, disabled } objects, or { label, items } groups whose items are either. A single field shows and posts the label and suiSelect reports the value; a multiple field holds and posts the value and draws the label on its chip. Property, not attribute.
placeholderplaceholderstringstring | undefinedText shown while the field is empty. A multiple field shows it only while it holds no chip.
readOnlyreadonlybooleanfalseDisplay only: the text and chips can be read and copied but not edited, the panel never opens, and the field still posts its value. Neither a read-only nor a disabled field holds the form on valueMissing.
requiredrequiredbooleanfalseMarks the field required for form validation: a single field needs text, a multiple field at least one value.
sizesize"lg" | "md" | "sm"'md'Field height: sm 28, md 34 (default), lg 42, from the --control-* tokens. A multiple field grows past it when its chips wrap. In the quiet variant it is the height of the box that paints on hover and while the field is in use; the text keeps the surrounding font.
tonetone"inverse" | undefinedInk for the quiet variant on the brand chrome. inverse takes the inverse inks, the inverse hover wash and the inverse focus colour, as sui-button's tone of the same name does, so the value reads on a tone="brand" toolbar. Opening the field shows its shell in the card's own inks. The default variant ignores it.
valuevalueSuiAutoCompleteValueSuiKey[] | string''The field's value. A single field holds its text, typed or picked: a pick writes the option's label here. A multiple field holds the picked option values, in the order they were added and each as its option gives it (a number stays a number), as an array property; anything that is not an array reads as no values. Mutable; written before suiInput or suiChange fires.
variantvariant"default" | "quiet"'default'default draws the field shell. quiet draws the text, or the chips, as they are in the surrounding font, with no border or fill at rest, for a record's properties column: the fill comes on hover, the Clear and Show options buttons show on hover, and the shell while the field has focus or the panel is open, since a focused text field is one being typed into. The label is not drawn but still names the field, so the host prints the property name. Typing, the keyboard, events, validation and the form post are the same in both. Anything else falls back to default.
virtualScrollThresholdvirtual-scroll-thresholdnumber200Suggestion count at which the panel starts mounting only the rows near the viewport instead of all of them, so a list of exactly this many is windowed. The keyboard, the ids and the counts a screen reader reads still cover every suggestion. Set it to 0 to keep every row in the DOM whatever the list costs, or to 1 to window a short list under test.

Events

EventDetailReactVueDescription
suiBlurvoidonSuiBlur@sui-blurFires when the input loses focus; no detail. Does not bubble, like the native blur.
suiChangeSuiAutoCompleteValueSuiKey[] | stringonSuiChange@sui-changeFires when the value commits; detail is the new value. Single field: the text, when it commits (pick, Enter, or leaving the field with a changed value). Multiple field: the full array of values, on every value added or removed.
suiCloseSuiCloseReason"cancel" | "close-button" | "escape" | "outside" | "pick" | "scrim" | "trigger"onSuiClose@sui-closeFires before the element closes the panel itself; detail is 'pick' | 'escape' | 'outside' | 'trigger' | 'cancel' ('pick' covers both picking a suggestion and committing the typed text with Enter, 'trigger' the dropdown button on an open panel, 'cancel' a form reset, a clear or an emptied field, 'outside' an outside pointer and Tab). Cancelable: preventDefault() keeps it open. Does not bubble: a preventDefault() in a dialog's own suiClose listener cannot veto this one.
suiFocusvoidonSuiFocus@sui-focusFires when the input takes focus; no detail. Every text field carries the same pair, so a host arms a hint or a launcher the same way on each without listening for the native focusin. Does not bubble, like the native focus.
suiInputstringonSuiInput@sui-inputFires when the text changes; detail is the text. On a multiple field the text is the query, and the values travel on suiChange. The element's own edits count as well as keystrokes: a pick that writes or empties the text, Clear, and a forceSelection wipe.
suiOpenvoidonSuiOpen@sui-openFires after the element opens the panel itself (typing, ArrowDown, the dropdown button, or focus under openOnFocus). Does not bubble, so a dialog around the field never takes it for its own.
suiSearchSuiAutoCompleteSearchDetailonSuiSearch@sui-searchFires when the field asks for suggestions; detail is { query, reason }. reason is input on each keystroke, with the typed text (a multiple field's query), and dropdown when the Show options button, or ArrowDown on an empty field, asks for every option, with the text in the field. A host that fetches answers it by setting options, usually with externalFilter set so the answer shows as given. The element's own edits (a pick, Clear) do not ask.
suiSelectSuiKeynumber | stringonSuiSelect@sui-selectFires when a suggestion is picked (click, Enter, or a label match when the text commits); detail is the option's value: the string itself, or value of an object option, as given. A multiple field fires it for each value added from the suggestions, not for one removed or added as typed.

Methods

MethodSignatureDescription
setFocus()setFocus() => Promise<void>Moves keyboard focus into the field, as el.focus() does.

Types

SuiAutoCompleteEntry typeOne entry of options: a string, an option object, or a group.
No members.
SuiAutoCompleteGroup interfaceA group: its header label and the suggestions under it. Groups never nest.
labelstring
itemsSuiAutoCompleteOptionInput[]
SuiAutoCompleteOption interfaceA suggestion with an identity of its own: label is what the field shows, value is what suiSelect reports and a multiple field holds, as given: a number stays a number.
labelstring
valueSuiKey
disabled?boolean
SuiAutoCompleteOptionInput type
No members.
SuiAutoCompleteSearchDetail interfaceWhat suiSearch carries: the text to answer and why the field asks.
querystringThe typed text (a multiple field's query), or the text in the field when the dropdown asks.
reasonSuiAutoCompleteSearchReason
SuiAutoCompleteSearchReason typeWhy the field asks for suggestions: input for typed text, dropdown for the Show options button, which asks for every option.
No members.
SuiAutoCompleteValue typeThe field's value: its text in a single field, the picked values in a multiple one.
No members.
SuiCloseReason typeWhy a dismissible surface is closing. Detail of the cancelable suiClose. A consumer expresses "a stray backdrop click must not close this" by calling preventDefault() on the reason it does not accept, which is why none of these needs a prop of its own.
No members.
SuiKey typeA record's or an item's key wherever the library identifies one: a row, a tree node, a list item, a selected value. Numbers stay numbers, so a numeric id round-trips through selection, the events and a framework binding without turning into a string.
No members.

Theming

Every design token this element can render with, read from its own stylesheets and the ones it imports. Set a token on :root or on any ancestor and every use below follows it. The values shown are the ones in the theme you are viewing. The list is read per stylesheet, so an element that imports a shared drawing module lists that module's tokens too, whether or not it paints with all of them.

See also