Select

Choose one or several values from a finite list, optionally with a search query.

<tp-select>
Select
Default density Compact density Restart from the initial state Show the copyable source

Usage

Select picks from a list that is too long, or too dynamic, to show as radios. Native option, optgroup and hr children declare the choices; the component renders its own list, keyboard navigation and typeahead, and submits the selected value under name. Use Native Select when the browser’s own picker is preferable, such as on mobile-first forms.

The default selected-item alignment is established when the popup opens. Hovering an option changes its highlight without moving the popup or scrolling the list. Keyboard navigation reveals the focused option within the list.

Item alignment attempts to place the selected/highlighted option against the Trigger. Collision correction also adjusts list scroll; unusable items or insufficient room fall back to ordinary placement.

Options

Each option contributes a value and its text. disabled keeps an option visible but unselectable, optgroup labels a group and hr draws a separator. placeholder shows while nothing is selected.

Groups and disabled options
Default density Compact density Restart from the initial state Show the copyable source

Native option, optgroup and hr sources are supported, including inherited optgroup disability.

Authored options

tp-select-option is the library’s own option element. It declares the same choice as a native option, accepts a non-string value when set as a property, and uses its text content as the label.

tp-select-option
Default density Compact density Restart from the initial state Show the copyable source
Urgent High Medium Low

Multiple

multiple keeps the popup open while options toggle, and submits one form entry per selected value in selection order.

Multiple
Default density Compact density Restart from the initial state Show the copyable source

Values stay unique and retain selection order.

Searchable

searchable swaps the trigger for an editable query that filters the list as people type. matching chooses contains (the default), prefix, exact or typo-tolerant fuzzy; highlight-matches marks the matched text. The query is never submitted, only the selection.

Searchable
Default density Compact density Restart from the initial state Show the copyable source

The query, selection and open state are independent.

Set a controlled property before connection to select that lane’s controlled mode. The owner accepts a proposal synchronously by updating the corresponding property in its callback. Leaving the proposal unaccepted or cancelling it restores that lane. Use defaultValue, defaultInputValue and defaultOpen for uncontrolled defaults; these are sampled initially and restored on form reset. multiple determines the selection shape: null or one value, versus an ordered unique array.

matching selects how the query matches options: contains (the default; locale collation, so case, accents and punctuation are ignored), prefix (every query word begins a word), exact, or fuzzy, which ranks options by relevance and tolerates typos and words typed out of order. Results come from, in order: filteredItems, a source, a filter, and otherwise matching; limit applies last.

Filtering retains selected values even when their options are not visible.

In list mode, highlighting does not change text. Both mode filters and shows a temporary selected completion suffix; inline completion keeps the item set static; none keeps it static without a completion. IME input does not navigate or commit prematurely. Arrow keys, Home/End and Enter operate through the editor’s active descendant while DOM focus stays in the editor.

Searchable multiple

With both searchable and multiple, selections show as removable chips beside the query. Backspace removes the last chip, and show-clear adds a button that clears the query or the selection.

Chips
Default density Compact density Restart from the initial state Show the copyable source

Contextual Clear clears a nonempty query, otherwise selection. The Clear action shows only while the lane it clears has something to clear. Both Clear proposes both lanes atomically: cancelling either proposal leaves both committed values unchanged. Chip Remove has an accessible value-specific name; directional keys move between chips, and Backspace/Delete remove through the same selection owner.

Grid

In a searchable Select with grid, columns lays each group’s options out in rows of that many, and the arrow keys move in two dimensions. Filtering reflows the rows. It suits short, scannable options such as sizes, flags or symbols.

Columns
Default density Compact density Restart from the initial state Show the copyable source

Grid records can specify row; actual Row containers match those logical rows. With columns, rows are formed instead from each group’s visible options in displayed order, so filtering reflows them. When a ranked search gathers options into their groups, the arrow keys follow the displayed order.

Data

Assign items when choices come from your data. Records take a value and label, groups take type: 'group' with nested items, and type: 'separator' divides them.

Items property
Default density Compact density Restart from the initial state Show the copyable source

A record’s label can be a Lit template for rich rows, such as an avatar or a badge. Give those records a plain text too; it drives typeahead and what the closed trigger reads.

Rich labels
Default density Compact density Restart from the initial state Show the copyable source

isItemEqual defines identity for object values; labels and the form’s serialized values are separate concerns. Form serialization uses String(value), including unmatched controlled values.

Later duplicate values are diagnosed and excluded from navigation and selection.

Object-valued choices use {value: record, label: 'Display text'}; use the factory for arbitrary application records with primitive IDs.

createSelectItems(data, {getValue, getLabel, diagnostic?}) lazily projects application records onto non-null primitive identifiers. It accepts absent loading data, flat data or groups, ignores holes and retains the first duplicate. Leaf records cannot themselves declare an items array. The returned collection provides value, itemLabel, hasValue and label (including equality and fallback arguments). createSelectFilter({locale?, sensitivity?, ignorePunctuation?, ...Intl.CollatorOptions}) exposes locale-aware contains, startsWith and endsWith.

Large lists

virtualized mounts only the rows near the visible part of the list while keeping its full scroll height, so hundreds or thousands of items open instantly. Keyboard navigation and typeahead still cover every option.

Virtualized time zones
Default density Compact density Restart from the initial state Show the copyable source

Virtualized mode requires the full ordered source. With mountedItems, you own the window: it controls only rendering, and onItemHighlighted supplies the source value, logical index and reason so you can bring an unmounted active record into view.

A source supplies the options for each query, such as a server endpoint. A query waits search-delay milliseconds after the last keystroke, a newer query aborts the previous one through signal, and the list shows a loading row meanwhile.

Search source
Default density Compact density Restart from the initial state Show the copyable source

With source, the source’s results are the options: a query waits searchDelay milliseconds without typing, a newer query aborts the previous one, previous results stay visible while loading, and searchStatus (idle, loading, loaded, error) with the tp-search-status and tp-search-error events report progress. While loading the list is busy and a status row (spinner and text) replaces the empty state; after a failure it shows the error text. The status region announces the same text.

Value changes

tp-value-change reports each proposed selection with its reason, and is cancelable. Read the committed value from value.

Value change
Default density Compact density Restart from the initial state Show the copyable source
Sorted by updated

Supply either defaultValue for an uncontrolled Select or value plus an owner that accepts proposals. Ownership is fixed when the component initializes. The getter exposes committed state, including during a callback. A controlled owner publishes an accepted proposal synchronously; a later listener can still veto the proposal. DOM events bubble across the shadow boundary.

select.value = 'apple'; // configure before connecting the element select.onValueChange = (event) => { if (event.defaultPrevented || event.detail.cancelled) return; select.value = event.detail.value; queueMicrotask(() => console.log('Committed:', select.value)); };

An accepted single selection closes on the item press, even when a controlled owner publishes the value later; multiple selection stays open. A canceled proposal keeps the popup open, and a controlled rejection keeps the committed markers and form value.

States

A Field’s disabled, invalid and error reach the Select. Read-only Selects still open and navigate, and keep their value in form data, but cannot change it.

Disabled, read-only and error
Default density Compact density Restart from the initial state Show the copyable source

In a dialog

A Select works inside a Dialog without extra setup: its popup belongs to the dialog, so it opens above the modal and stays interactive while the rest of the page is inert.

Select in a dialog
Default density Compact density Restart from the initial state Show the copyable source
Move issue Move

Inline mode renders an in-flow list without its own portal, positioner or popup; accepted closing of its surrounding Dialog, Alert Dialog, Drawer or Popover resets transient query/highlight.

Keyboard and composition

Enter, Space or Alt+ArrowDown opens. Arrow keys move through enabled options; Home/End go to the boundaries. Typeahead appends exact keys, matches a locale lower-case prefix, and resets after 750 ms, on opening, or immediately after unmatched non-space input. Repeated-key cycling is available only when no non-null label starts with two identical characters. Printable text performs typeahead and does not filter the list. Composition input does not select. Enter/Space activates the highlighted option. Escape closes without selecting; Tab closes and continues normal traversal. Scroll controls respond only while their direction has overflow and stop repeating when the pointer leaves, the popup closes or the host detaches.

Use actual tp-field for labels/descriptions/errors, tp-icon for rich choice artwork, tp-button for adjacent actions and tp-dialog for modal composition.

Inline

Inline
Default density Compact density Restart from the initial state Show the copyable source

variant="inline" fits the Select into one row the height of a control, with label at the start and the selection at the end, so it stacks with other inline controls such as an inline Slider. The list opens below the row and selects as usual. Inside a Field, the Field’s label moves into the row. A searchable Select keeps its usual look.

API Reference

Every element also accepts motion-policy.

Attributes

AttributePropertyTypeDefaultDescriptionsearchablesearchablebooleanfalseReplaces the trigger with an editable query that filters the list as people type. The query is never submitted.input-valueinputValuestring—The query of a searchable Select. Setting it controls the query: accept proposals in onInputValueChange.default-input-valuedefaultInputValuestring | undefined—The initial query of an uncontrolled searchable Select.queryquerystring—Another name for inputValue.matchingmatchingSearchMatching'contains'How the query matches items: contains (default), prefix, exact or ranked fuzzy.search-delaysearchDelaynumber0Milliseconds without typing before a source is queried.highlight-matcheshighlightMatchesbooleanfalseMarks matched ranges in option text.submit-on-item-clicksubmitOnItemClickbooleanfalseSubmits the owning form after an item is accepted (selection-free mode).limitlimitnumber-1The most matching options listed at once; a negative number lists them all.localelocalestring | undefined—Locale of matching and typeahead, when it differs from the page's.completion-modecompletionModeSelectCompletionMode'list'How the query completes: list filters the list, inline previews the highlighted option's text after the caret, both does both and none neither.clear-behaviorclearBehaviorSelectClearBehavior'contextual'What the clear button clears: the query, the selection, both, or (contextual) the query while there is one and otherwise the selection.auto-highlightautoHighlightboolean | 'always'falseHighlights the first match, so Enter selects it: after typing (true) or whenever the list shows options (always).keep-highlightkeepHighlightbooleanfalseKeeps the highlighted option while the query changes, as long as it still matches.loop-focusloopFocusbooleantrueLets the arrow keys wrap from the last option to the first and back.gridgridbooleanfalseLays options out in rows that the arrow keys move through in two dimensions; rows come from columns or each option's row.columnscolumnsnumber | undefined—Grid mode: rows of this many options per group, formed from the displayed order.inlineinlinebooleanfalseRenders a searchable Select's list in the page flow under the query, always shown, instead of in a popup.virtualizedvirtualizedbooleanfalseMounts only the rows near the list's viewport. Without mountedItems the Select owns the window; with it, the consumer does.busybusybooleanfalseConsumer-declared busy state (for example while options load); shows the loading status row.open-on-input-clickopenOnInputClickbooleantrueOpens the list when the query is clicked.close-on-selectcloseOnSelectboolean | undefined—Whether choosing an option closes the list; unset, a single selection closes it and a multiple one keeps it open.show-triggershowTriggerbooleantrueShows the button beside a searchable query that opens the list.show-clearshowClearbooleanfalseShows a button that clears what clearBehavior names, while there is something to clear.show-chip-removeshowChipRemovebooleantrueShows a remove button on each chip of a searchable multiple Select.clear-keep-mountedclearKeepMountedbooleanfalseKeeps the clear button mounted, hidden, while there is nothing to clear.default-valuedefaultValueunknown—The initial selection of an uncontrolled Select, restored on form reset; an array with multiple.openopenboolean—Whether the list is open. Setting it controls the open state: accept proposals in onOpenChange.default-opendefaultOpenbooleanfalseOpens the list on first render without controlling open.multiplemultiplebooleanfalseSelects several values: the value becomes an array in selection order, and the list stays open while options toggle.placeholderplaceholderstring''Shown while nothing is selected, or in an empty query.labellabelstring''Name of the trigger and the list when no Field labels the Select, and the label an inline Select shows; without one they are named “Options”.variantvariantSelectVariant'default'inline shows the label and the selection in one compact control-height row, for stacked controls; an enclosing Field's label moves into the row. A searchable Select keeps its look.identifieridentifierstring—ID of the trigger or the query; the list takes a related ID.autocompleteautocompletestring''Autofill hint for the selection, such as country.no-autofillnoAutofillbooleanfalseOpts the autofill receiver and the query editor out of host and extension autofill.native-actionnativeActionbooleantrueKeeps the trigger a native button; turn it off when a render delegate replaces the trigger with another element.modalmodalboolean—Makes the rest of the page inert and locks its scrolling while the list is open; off by default for a searchable Select.highlight-item-on-hoverhighlightItemOnHoverbooleantrueMoves the highlight with the pointer.keep-mountedkeepMountedbooleanfalseKeeps the closed popup in the DOM, hidden and inert.align-item-with-triggeralignItemWithTriggerboolean—Places the popup so the selected option sits over the trigger; off by default for a searchable or inline Select, whose list opens below the trigger with the popup entrance.placementplacementstring'block-end start'Side and alignment of the popup against the trigger, in logical words such as block-end start.sidesideLogicalSide | undefined—Overrides the side of placement.alignalignAlignment | undefined—Overrides the alignment of placement.side-offsetsideOffsetGeometryOffset—Gap between the trigger and the popup, in pixels or from a function of the geometry.align-offsetalignOffsetGeometryOffset0Shift of the popup along its alignment, in pixels or from a function of the geometry.disable-anchor-trackingdisableAnchorTrackingbooleanfalseStops following the trigger as it moves; call updatePosition() instead.stickystickybooleanfalseKeeps the popup in view after the trigger scrolls out of the boundary.position-methodpositionMethodPositioningStrategy'absolute'Positions the popup with absolute or fixed CSS positioning.scroll-up-keep-mountedscrollUpKeepMountedbooleanfalseKeeps the scroll-up arrow mounted while the list cannot scroll up.scroll-down-keep-mountedscrollDownKeepMountedbooleanfalseKeeps the scroll-down arrow mounted while the list cannot scroll down.show-arrowshowArrowbooleanfalseDraws an arrow from the popup to the trigger.arrow-paddingarrowPaddingnumber—Closest distance between the arrow and the popup's corners.arrow-widtharrowWidthnumber—Width of the arrow's base.arrow-heightarrowHeightnumber—Length of the arrow from base to tip.arrow-tip-radiusarrowTipRadiusnumber0Rounds the arrow's tip.arrow-patharrowPathstring''An SVG path that draws the arrow instead of the default triangle.arrow-border-colorarrowBorderColorstring''Color of the arrow's outline.arrow-border-widtharrowBorderWidthnumber0Width of the arrow's outline.show-backdropshowBackdropbooleanfalseDraws a backdrop behind the open popup.

Properties

PropertyTypeDefaultDescriptionitemsreadonly SelectEntry[] | SelectItemCollection<unknown> | undefined—The choices as data, replacing the children: option records, groups, separators, plain values, or a collection from createSelectItems.filteredItemsreadonly unknown[] | undefined—The options to show for the current query, when the application filters them itself.filter((item: unknown, query: string, text: (item: unknown) => string) => boolean) | null | undefined—A custom test of an item against the query; null turns filtering off.sourceSearchSource | undefined—Supplies results instead of matching items: a server endpoint or another search engine.matchFieldsSelectMatchFields | undefined—Extra searchable text of an item, such as keywords.messagesSelectMessages{}Replaceable status and empty-state texts.mountedItemsreadonly unknown[] | undefined—With virtualized, the slice of items the application renders itself; unset, the Select chooses the rendered rows.onInputValueChange((event: TpValueChangeEvent<string>) => void) | undefined—Called with each proposed query change, which is also dispatched as tp-input-value-change; a controlled owner accepts it by setting inputValue.onItemHighlighted((value: unknown, details: { index: number; reason: ChangeReason }) => void) | undefined—Called when the highlight moves, with the option's value, its index and the reason.searchStatusread-onlySearchStatus—Status of the search source behind the list; idle when matching locally.resolvedMessagesread-onlyRequired<SelectMessages>—messages merged over the defaults.effectiveNoAutofillread-onlyboolean—Whether autofill is off, from no-autofill here or on an enclosing Field or Form.containerSelectContainernullAn element to render the popup into, instead of the Select's own top layer.anchorSelectAnchornullAn element or virtual rectangle the popup positions against instead of the trigger.collisionAvoidanceCollisionPolicy{ side: 'flip', align: 'flip', fallbackAxisSide: 'none' }How the popup avoids the edges of its boundary: flipping or shifting its side and alignment.collisionBoundaryCollisionBoundary'clipping-ancestors'The area the popup stays inside: the clipping ancestors, elements or a rectangle.collisionPaddingnumber | Partial<Record<'top' | 'bottom' | 'left' | 'right', number>>—Space kept between the popup and its boundary, overall or per side.initialFocusSelectFocusTarget'trigger'What receives focus when the list opens: the trigger, the first option, the popup, an element or nothing.finalFocusSelectFocusTarget'trigger'Where focus goes when the list closes: the trigger, the previously focused element, an element or nowhere.isItemEqual((a: unknown, b: unknown) => boolean) | undefined—Whether two values are the same choice, for object values that are not identical between renders.itemToText((value: unknown) => string) | undefined—Text of a value, for typeahead, matching, the closed trigger and form submission.itemToLabel((value: unknown) => unknown) | undefined—Rich label of a value whose option has none of its own.onValueChange((event: TpValueChangeEvent<unknown>) => void) | undefined—Called with each proposed value before tp-value-change; a controlled owner accepts it by setting value.onOpenChange((event: TpSurfaceOpenChangeEvent) => void) | undefined—Called with each proposed opening or closing before tp-open-change; a controlled owner accepts it by setting open.onOpenChangeComplete((open: boolean) => void) | undefined—Called after the list finishes opening or closing.actionsread-onlySelectActions{ open: () => this.setOpen(true), close: () => this.setOpen(false), unmount: () => this.unmount(), }An imperative handle with open(), close() and unmount(), to pass where a function is expected.triggerElementread-onlyHTMLElement | null—The rendered trigger, which takes focus and the label.popupElementread-onlyHTMLElement | null—The rendered popup, or null while it is not mounted.listElementread-onlyHTMLElement | null—The rendered list, or null while it is not mounted.highlightedValueread-onlyunknown—Value of the highlighted option.presenceStateread-onlystring—Where the popup is in its lifecycle: absent, starting, open, ending or retained.

Events

EventTypeDescriptiontp-value-changeTpValueChangeEvent<unknown>Cancelable proposal of a new value, before it commits; detail.value, detail.previousValue and detail.reason.tp-field-valueCustomEvent<{ value: unknown; previousValue: unknown; reason: ChangeReason; }>After a value commits; an enclosing Field tracks its dirty state from it.tp-open-changeTpSurfaceOpenChangeEventCancelable proposal to open or close the list; detail.value and detail.reason.tp-open-change-completeCustomEvent<{ open: boolean; }>{ open } after the list finishes opening or closing.tp-search-statusCustomEvent<{ status: SearchStatus; query: string; total: number; }>{ status, query, total } as a search source loads, settles or fails.tp-search-errorCustomEvent<{ error: unknown; query: string; }>{ error, query } when a search source fails.

Methods

MethodReturnsDescriptionclear(event?)voidProposes clearing what clearBehavior names, as the clear button does.removeChip(value, event?)voidProposes removing one value from a multiple selection, as its chip's remove button does.setOpen(open, reason, event?)voidProposes opening or closing the list, with a reason and source event, as a cancelable tp-open-change.close()voidProposes closing the list.unmount()voidCloses the list and removes popup content that keepMounted retained.updatePosition()Promise<void>Measures and positions the popup again, for when anchor tracking is off.

Slots

SlotDescriptiondefaultNative option, optgroup and hr children, or Select Options, declaring the choices when items is unset.emptyShown in place of the options when none matches the query.

CSS parts

PartDescription::part(select-action-host)The element around each trigger, clear and chip-remove button; style the buttons through their own parts.::part(select)The root wrapper around the trigger or the query.::part(select-chip-remove)The button that removes a chip.::part(select-clear)The button that clears the query or the selection.::part(select-collection)The options inside a searchable Select's list.::part(select-content)The popup that holds the list.::part(select-empty-state)The text shown when no option matches the query.::part(select-group)A group of options.::part(select-label)The label of a group.::part(select-list)The scrolling list of options.::part(select-match)The part of an option's text that matches the query.::part(select-option)One option, with its text and check mark.::part(select-scroll-down-button)The arrow that scrolls the list down while it overflows below.::part(select-scroll-up-button)The arrow that scrolls the list up while it overflows above.::part(select-separator)The line between options or groups.::part(select-status)The loading or error row of a search source.::part(select-trigger)The button that shows the selection and opens the list.::part(select-trigger-label)The label at the start of an inline trigger.::part(select-value)The selection or the placeholder inside the trigger.

<tp-select-option>

Attributes

AttributePropertyTypeDefaultDescriptionvaluevalueunknownundefinedThe value the option selects: text as an attribute, any value as a property.indexindexnumber | undefined—Position of the option in the application's data, reported when it is highlighted.rowrownumber | undefined—The row the option belongs to in a grid Select.native-actionnativeActionbooleanfalseRenders the option as a native button rather than a plain element.indicator-keep-mountedindicatorKeepMountedbooleanfalseKeeps the check mark mounted, hidden, while the option is not selected.

Properties

PropertyTypeDefaultDescriptionlabelunknownundefinedA rich label, such as a Lit template, shown instead of the text content.onClick((event: MouseEvent) => void) | undefined—Called when the rendered option is clicked.textContractComponentPartContract | undefined—Part contract of the option's text.indicatorContractComponentPartContract | undefined—Part contract of the option's check mark.

Events

EventTypeDescriptiontp-select-source-changeEventBubbles after the declaration changes, so the Select reads it again.

Slots

SlotDescriptiondefaultThe option's text: its label when label is unset, and its typeahead text.

Components, widgets and patterns that use this one.