Select
Accessible, unstyled custom Select: a select-only combobox with a typed listbox popup.
|
|
| Package |
@vizejs/ui/select |
| Maturity |
stable |
| Own the source |
vize lib pull select |
| Requires |
collection, composite-navigation, context, controllable-state, dismissable-layer, id, portal, positioner, presence, typeahead, virtualizer |
| Aliases |
select, custom select, dropdown select, select-only combobox, picker |
| Covers |
WAI-ARIA select-only combobox pattern, Radix/Reka UI Select, React Aria Select, Headless UI Listbox, Ark UI Select |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { Select, SelectRoot, SelectContent, SelectGroup, SelectItem, SelectItemIndicator, SelectLabel, SelectScrollButton, SelectSeparator, SelectTrigger, SelectValue, SelectViewport, SelectVirtualizer } from "@vizejs/ui/select";
Or copy the source into your project with vize lib pull select (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Controlled select-only combobox with a placeholder, a labelled group, and selection marks. -->
<script setup lang="ts">
import { ref, useId } from "vue";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectItemIndicator,
SelectLabel,
SelectTrigger,
SelectValue,
SelectViewport,
} from "@vizejs/ui/select";
const timezones = ["Asia/Tokyo", "Europe/Paris", "America/New_York"];
const timezone = ref<string | null>(null);
const labelId = useId();
</script>
<template>
<div>
<span :id="labelId">Time zone</span>
<Select v-model="timezone" :items="timezones" name="timezone" placeholder="Choose a time zone">
<SelectTrigger :aria-labelledby="labelId"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectViewport>
<SelectGroup>
<SelectLabel>Common zones</SelectLabel>
<SelectItem v-for="zone in timezones" :key="zone" :value="zone">
{{ zone }}
<SelectItemIndicator>✓</SelectItemIndicator>
</SelectItem>
</SelectGroup>
</SelectViewport>
</SelectContent>
</Select>
</div>
</template>
API
Select<T, Multiple extends boolean = false>
Source: src/families/selection/select/select-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
modelValue |
SelectModelValue<T, Multiple> |
undefined |
Controlled selection: T | null in single mode, readonly T[] when multiple is true. undefined selects uncontrolled behavior. |
defaultValue |
SelectModelValue<T, Multiple> |
undefined |
Initial selection for uncontrolled use and the value restored by reset. |
multiple |
Multiple |
false |
Allow selecting several options. The literal type of this prop decides the model type. |
items |
readonly T[] |
undefined |
Every option value, in display order. Optional for compound use; enables typed inference, closed-state labels, and virtualized navigation. |
by |
SelectBy<T> |
undefined |
Compare values by a property key or with a custom equality function. |
itemText |
(value: T) => string |
undefined |
Human-readable text for a value, used by SelectValue, typeahead, and SSR labels. |
itemDisabled |
(value: T) => boolean |
undefined |
Disable individual values declared through items. |
formValue |
(value: T) => string |
undefined |
Serialize a value for native form submission. |
open |
boolean |
undefined |
Controlled open state. undefined selects uncontrolled behavior. |
defaultOpen |
boolean |
false |
Initial open state for uncontrolled use. |
disabled |
boolean |
false |
Disable the trigger, options, and form submission. |
required |
boolean |
false |
Require a selection for native constraint validation. |
name |
string |
undefined |
Form control name used by the hidden native select. |
form |
string |
undefined |
Id of the form owner when the select renders outside its <form>. |
autocomplete |
string |
undefined |
Native autofill hint forwarded to the hidden select. |
placeholder |
string |
undefined |
Text shown by SelectValue while nothing is selected. |
loop |
boolean |
false |
Wrap arrow-key navigation at the first and last option. |
typeaheadTimeout |
number |
500 |
Idle time before buffered typeahead starts a new query. |
selectOnTab |
boolean |
false |
Commit the highlighted option when Tab closes a single-selection popup (APG select-only combobox). |
closeOnSelect |
boolean |
undefined |
Close the popup after an option is chosen. undefined closes in single mode only. |
dir |
SelectDirection |
"ltr" |
Reading direction published to parts. |
ariaInvalid |
SelectAriaInvalid |
false |
Invalid state announced to assistive technology. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: SelectModelValue<T, Multiple>] |
Fired when the selection requests a new controlled value (T | null or readonly T[]). |
change |
[ value: SelectModelValue<T, Multiple>, previous: SelectModelValue<T, Multiple>, nativeEvent: Event, ] |
Fired after a user interaction changes the selection: next value, previous value, and the native event. |
update:open |
[open: boolean] |
Fired when the popup requests a controlled open value. |
open-change |
[open: boolean, previous: boolean, nativeEvent: Event | null] |
Fired after any distinct open-state request with the next state, previous state, and triggering event. |
Slots
| Slot |
Slot props |
Description |
default |
SelectSlotState<T> |
Compound Select parts. Receives the current selection and open state. |
Exposed
| Member |
Type |
Description |
clear |
— |
|
deselect |
— |
|
disabled |
— |
|
focus |
— |
|
id |
— |
|
invalid |
— |
|
listboxId |
— |
|
open |
— |
|
required |
— |
|
reset |
— |
|
select |
— |
|
selected |
— |
|
selectedText |
— |
|
selectionMode |
— |
|
setOpen |
— |
|
state |
— |
|
triggerId |
— |
|
SelectRoot<T, Multiple extends boolean = false>
Source: src/families/selection/select/select-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
modelValue |
SelectModelValue<T, Multiple> |
undefined |
Controlled selection: T | null in single mode, readonly T[] when multiple is true. undefined selects uncontrolled behavior. |
defaultValue |
SelectModelValue<T, Multiple> |
undefined |
Initial selection for uncontrolled use and the value restored by reset. |
multiple |
Multiple |
false |
Allow selecting several options. The literal type of this prop decides the model type. |
items |
readonly T[] |
undefined |
Every option value, in display order. Optional for compound use; enables typed inference, closed-state labels, and virtualized navigation. |
by |
SelectBy<T> |
undefined |
Compare values by a property key or with a custom equality function. |
itemText |
(value: T) => string |
undefined |
Human-readable text for a value, used by SelectValue, typeahead, and SSR labels. |
itemDisabled |
(value: T) => boolean |
undefined |
Disable individual values declared through items. |
formValue |
(value: T) => string |
undefined |
Serialize a value for native form submission. |
open |
boolean |
undefined |
Controlled open state. undefined selects uncontrolled behavior. |
defaultOpen |
boolean |
false |
Initial open state for uncontrolled use. |
disabled |
boolean |
false |
Disable the trigger, options, and form submission. |
required |
boolean |
false |
Require a selection for native constraint validation. |
name |
string |
undefined |
Form control name used by the hidden native select. |
form |
string |
undefined |
Id of the form owner when the select renders outside its <form>. |
autocomplete |
string |
undefined |
Native autofill hint forwarded to the hidden select. |
placeholder |
string |
undefined |
Text shown by SelectValue while nothing is selected. |
loop |
boolean |
false |
Wrap arrow-key navigation at the first and last option. |
typeaheadTimeout |
number |
500 |
Idle time before buffered typeahead starts a new query. |
selectOnTab |
boolean |
false |
Commit the highlighted option when Tab closes a single-selection popup (APG select-only combobox). |
closeOnSelect |
boolean |
undefined |
Close the popup after an option is chosen. undefined closes in single mode only. |
dir |
SelectDirection |
"ltr" |
Reading direction published to parts. |
ariaInvalid |
SelectAriaInvalid |
false |
Invalid state announced to assistive technology. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: SelectModelValue<T, Multiple>] |
Fired when the selection requests a new controlled value (T | null or readonly T[]). |
change |
[ value: SelectModelValue<T, Multiple>, previous: SelectModelValue<T, Multiple>, nativeEvent: Event, ] |
Fired after a user interaction changes the selection: next value, previous value, and the native event. |
update:open |
[open: boolean] |
Fired when the popup requests a controlled open value. |
open-change |
[open: boolean, previous: boolean, nativeEvent: Event | null] |
Fired after any distinct open-state request with the next state, previous state, and triggering event. |
Slots
| Slot |
Slot props |
Description |
default |
SelectSlotState<T> |
Compound Select parts. Receives the current selection and open state. |
Exposed
| Member |
Type |
Description |
selected |
readonly T[] |
Selected values in selection order. |
selectedText |
readonly string[] |
Display text for each selected value. |
open |
boolean |
Whether the popup is open. |
disabled |
boolean |
Whether the select is disabled. |
required |
boolean |
Whether a selection is required. |
invalid |
boolean |
Whether the select is invalid (prop or failed native validation). |
selectionMode |
SelectSelectionMode |
Single or multiple selection. |
state |
SelectState |
Stable state token. |
id |
string |
Root-owned base id. |
triggerId |
string |
Id of the trigger element. |
listboxId |
string |
Id of the listbox element. |
setOpen |
(open: boolean, event?: Event | null) => boolean |
Request a specific open state. |
select |
(value: T) => boolean |
Select a value (toggle in multiple mode) and report whether it changed. |
deselect |
(value: T) => boolean |
Deselect a value and report whether it changed. |
clear |
() => boolean |
Clear the selection and report whether it changed. |
reset |
() => boolean |
Restore defaultValue and report whether it changed. |
focus |
(options?: FocusOptions) => void |
Move focus to the trigger. |
SelectContent
Source: src/families/selection/select/select-content.vue
Props
| Prop |
Type |
Default |
Description |
position |
SelectPosition |
"popper" |
popper anchors below the trigger; item-aligned overlays the selected option on the trigger. |
placement |
Placement |
"bottom-start" |
Preferred popper placement before collision handling. |
strategy |
PositionerStrategy |
"fixed" |
CSS positioning mode published on the floating host. |
offset |
number |
4 |
Gap between trigger and listbox in popper mode. |
collisionPadding |
number |
8 |
Viewport padding the listbox should not cross. |
flip |
boolean |
true |
Flip to the opposite side when the preferred side overflows more (popper mode). |
shift |
boolean |
true |
Shift the listbox back into the viewport. |
size |
boolean |
true |
Publish available-size CSS variables and constrain the host. |
hide |
boolean |
true |
Hide when the trigger scrolls out of view. |
forceMount |
boolean |
false |
Keep options mounted (hidden) while closed so labels and typeahead work before first open. |
to |
string | HTMLElement |
"body" |
CSS selector or element the popup is moved into. |
portalDisabled |
boolean |
false |
Render in place instead of teleporting. |
defer |
boolean |
true |
Keep content in place until the portal target exists, avoiding SSR mismatch. |
closeOnEscape |
boolean |
true |
Let Escape close the popup. |
closeOnPointerDownOutside |
boolean |
true |
Let an outside pointer-down close the popup. |
ariaLabel |
string |
undefined |
Accessible name for the listbox when the trigger label is not enough. |
ariaLabelledby |
string |
undefined |
Space-separated ids labelling the listbox. Defaults to the trigger. |
Events
| Event |
Payload |
Description |
escape-key-down |
[event: SelectEscapeKeyDownEvent] |
Fired before Escape closes the popup. Call preventDefault() to keep it open. |
pointer-down-outside |
[event: SelectPointerDownOutsideEvent] |
Fired before an outside pointer-down closes the popup. Call preventDefault() to keep it open. |
dismiss |
[event: SelectDismissEvent] |
Fired after an unprevented dismissal request closes the popup. |
Slots
| Slot |
Slot props |
Description |
default |
SelectContentSlotState |
Options, groups, separators, viewport, and scroll buttons. Receives open state and placement. |
Exposed
| Member |
Type |
Description |
element |
HTMLDivElement | null |
Rendered listbox element. |
SelectGroup
Source: src/families/selection/select/select-group.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned group id. null and undefined select a deterministic fallback. |
ariaLabel |
string |
undefined |
Accessible group name used instead of a SelectLabel. |
Slots
| Slot |
Slot props |
Description |
default |
— |
A SelectLabel followed by the group's options. |
SelectItem<T>
Source: src/families/selection/select/select-item.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned option id. null and undefined select a deterministic fallback. |
value (required) |
T |
required |
Option value compared with the root selection through by. |
disabled |
boolean |
false |
Disable this option. |
textValue |
string |
undefined |
Explicit label for typeahead and SelectValue. undefined extracts the option text. |
index |
number |
undefined |
Absolute index inside the root items, required inside SelectVirtualizer. |
ariaLabel |
string |
undefined |
Accessible name when the option text is not enough. |
Slots
| Slot |
Slot props |
Description |
default |
SelectItemSlotState<T> |
Option content. Receives selected, active, and disabled state. |
Exposed
| Member |
Type |
Description |
element |
HTMLDivElement | null |
Rendered option element. |
value |
T |
Option value. |
selected |
boolean |
Whether this option is selected. |
active |
boolean |
Whether this option is highlighted. |
select |
() => boolean |
Choose this option as if clicked. |
SelectItemIndicator
Source: src/families/selection/select/select-item-indicator.vue
Props
| Prop |
Type |
Default |
Description |
forceMount |
boolean |
false |
Render while the option is unselected too, publishing data-state for CSS transitions. |
Slots
| Slot |
Slot props |
Description |
default |
{ readonly selected: boolean } |
Indicator content such as a check mark. Receives the option selection state. |
SelectLabel
Source: src/families/selection/select/select-label.vue
Slots
| Slot |
Slot props |
Description |
default |
— |
Visible group heading text. |
Source: src/families/selection/select/select-scroll-button.vue
Props
| Prop |
Type |
Default |
Description |
direction (required) |
SelectScrollDirection |
required |
Scroll direction driven while hovered or pressed. |
step |
number |
32 |
Pixels scrolled per tick. |
interval |
number |
50 |
Milliseconds between ticks while hovered or pressed. |
Slots
| Slot |
Slot props |
Description |
default |
{ readonly direction: SelectScrollDirection } |
Decorative arrow content. Receives the scroll direction. |
Exposed
| Member |
Type |
Description |
scrollOnce |
— |
|
update |
— |
|
visible |
— |
|
SelectSeparator
Source: src/families/selection/select/select-separator.vue
Slots
| Slot |
Slot props |
Description |
default |
— |
Optional decorative separator content. |
SelectTrigger
Source: src/families/selection/select/select-trigger.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name when no visible label supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids of visible labels for the select. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the select. |
ariaErrormessage |
string |
undefined |
Id of the validation message announced while invalid. |
Slots
| Slot |
Slot props |
Description |
default |
{ readonly open: boolean; readonly disabled: boolean; readonly empty: boolean; } |
Trigger content, usually SelectValue plus an icon. Receives open and selection state. |
Exposed
| Member |
Type |
Description |
element |
HTMLButtonElement | null |
Rendered trigger button. |
focus |
(options?: FocusOptions) => void |
Move focus to the trigger. |
SelectValue
Source: src/families/selection/select/select-value.vue
Props
| Prop |
Type |
Default |
Description |
separator |
string |
", " |
Text placed between labels when several values are selected. |
Slots
| Slot |
Slot props |
Description |
default |
SelectValueSlotState<unknown> |
Custom selected-value rendering. Receives the selected values, their text, and the placeholder. |
placeholder |
SelectValueSlotState<unknown> |
Custom placeholder rendering shown while nothing is selected. |
SelectViewport
Source: src/families/selection/select/select-viewport.vue
Slots
| Slot |
Slot props |
Description |
default |
— |
Scrollable options region. Scroll buttons and SelectVirtualizer observe this element. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
SelectVirtualizer<T>
Source: src/families/selection/select/select-virtualizer.vue
Props
| Prop |
Type |
Default |
Description |
items (required) |
readonly T[] |
required |
Every option value in display order; only the visible window is rendered. |
estimateItemSize |
number |
32 |
Estimated option height in CSS pixels used until options are measured. |
overscan |
number |
4 |
Options rendered beyond each edge of the visible window. |
initialViewportHeight |
number |
256 |
Viewport height assumed for server rendering and before measurement. |
getItemKey |
(item: T, index: number) => string | number |
undefined |
Stable render key for an option. Defaults to its index. |
Slots
| Slot |
Slot props |
Description |
default |
SelectVirtualItemSlotState<T> |
Render one SelectItem with :value="item" and :index="index". |
Exposed
| Member |
Type |
Description |
scrollToIndex |
— |
|
totalSize |
— |
|
virtualItems |
— |
|
Behavior
Contract
Select is a headless, strongly typed custom select built on the WAI-ARIA APG
select-only combobox pattern. select-root.vue is generic over the option
value type T and a Multiple literal: omitting multiple (or passing
false) makes the model T | null; multiple: true makes it readonly T[].
Values may be any type; by compares them by property key or custom equality.
select-trigger.vue renders a native <button role="combobox"> that keeps DOM
focus for the whole interaction and exposes the highlighted option through
aria-activedescendant. select-content.vue renders the role="listbox"
popup through Portal, Presence, Positioner, and a dismissable layer; options
(select-item.vue) register with the shared collection registry and composite
navigation used by Listbox. select-value.vue renders the selected label or
placeholder, select-item-indicator.vue a selection mark,
select-group.vue/select-label.vue labelled groups, select-separator.vue a
decorative divider, select-viewport.vue the scroll container,
select-scroll-button.vue hover-to-scroll affordances, and
select-virtualizer.vue a windowed option list for large collections.
The root always renders a hidden, aria-hidden native <select> that mirrors
the selection for form submission, required validation, form= association,
form reset, and autofill. No CSS is shipped; every part publishes
data-vize-ui, data-state, and state-specific data attributes.
Public Surface
| Surface |
Contract |
SelectRoot props |
id, modelValue, defaultValue, multiple, items, by, itemText, itemDisabled, formValue, open, defaultOpen, disabled, required, name, form, autocomplete, placeholder, loop, typeaheadTimeout, selectOnTab, closeOnSelect, dir, ariaInvalid |
SelectRoot emits |
update:modelValue(value), change(value, previous, nativeEvent), update:open(open), open-change(open, previous, nativeEvent) |
SelectRoot expose |
id, triggerId, listboxId, selected, selectedText, open, disabled, required, invalid, selectionMode, state, setOpen, select, deselect, clear, reset, focus |
SelectTrigger props |
ariaLabel, ariaLabelledby, ariaDescribedby, ariaErrormessage |
SelectValue props/slots |
separator; slots default(state), placeholder(state) |
SelectContent props |
position, placement, strategy, offset, collisionPadding, flip, shift, size, hide, forceMount, to, portalDisabled, defer, closeOnEscape, closeOnPointerDownOutside, ariaLabel, ariaLabelledby |
SelectContent emits |
escape-key-down(event), pointer-down-outside(event), dismiss(event) |
SelectItem props |
id, value, disabled, textValue, index, ariaLabel |
SelectVirtualizer props |
items, estimateItemSize, overscan, initialViewportHeight, getItemKey |
| Parts |
root, trigger, value, content-host, content, viewport, item, item-indicator, group, label, separator, scroll-button, virtualizer, native |
| Root data attributes |
data-vize-ui="select", data-state, data-selection-mode, data-disabled, data-required, data-invalid, data-empty |
| Item data attributes |
data-vize-ui="select-item", data-state="checked|unchecked", data-highlighted, data-disabled, data-index |
Normative Behavior
| # |
State |
Input |
Outcome |
Proven by |
| S1 |
closed, empty |
render |
trigger is a button with role="combobox", aria-haspopup="listbox", aria-expanded="false", no aria-controls; value shows the placeholder |
renders select-only combobox semantics with placeholder and closed popup |
| S2 |
closed |
click trigger |
opens, wires aria-controls to the listbox, highlights the selected option through aria-activedescendant |
click opens the listbox, highlights the selected option, and click selects |
| S3 |
open, single |
click enabled option |
selects it, emits update:modelValue and change(value, previous, event), closes, value shows its label |
click opens the listbox, highlights the selected option, and click selects |
| S4 |
open |
pointer move over option |
highlights enabled options only; disabled options neither highlight nor select; option pointer-down never steals trigger focus |
pointer movement highlights enabled options without selecting |
| S5 |
open, multiple |
click option |
toggles it, emits a frozen readonly T[], keeps the popup open |
multiple selection toggles values, keeps the popup open, and emits arrays |
| S6 |
template multiple |
attribute form ("") |
resolves to multiple mode |
the attribute form of multiple selects multiple mode |
| S7 |
controlled |
selection or open request |
emits the request and renders the parent value; by matches structurally equal values |
controlled value and open state wait for the parent |
| S8 |
by comparator |
render / open |
comparator decides selection; labels fall back to label/name/title/text fields |
a comparator \by` and object text fallbacks resolve selection and labels` |
| S9 |
disabled root |
ArrowDown / click |
trigger is natively disabled and never opens |
disabled roots do not open and disabled options are skipped |
| S10 |
groups |
render |
role="group" labelled by its SelectLabel or ariaLabel; separators are aria-hidden |
groups wire labels and separators stay out of the accessibility tree |
| S11 |
any |
exposed API |
select, deselect, clear, reset, setOpen, focus share the same state |
exposed methods drive the same selection and open state |
| S12 |
no provider |
mount a part |
throws VIZE_UI_CONTEXT_MISSING |
parts require a Select provider |
| K1 |
closed |
ArrowDown / ArrowUp / Enter / Space |
prevents default, opens, highlights the selected option (or the first) |
closed "ArrowDown" opens and highlights the selected option and siblings |
| K2 |
closed, empty |
ArrowDown |
highlights the first enabled option |
closed ArrowDown without a selection highlights the first option |
| K3 |
closed |
Home / End |
opens on the first / last enabled option |
closed Home and End open on the first and last enabled option |
| K4 |
closed / open |
printable character |
opens and runs buffered typeahead; Space extends a pending query instead of selecting |
closed printable characters open and run typeahead |
| K5 |
open |
ArrowDown / ArrowUp / Home / End / Page |
moves the highlight, skipping disabled options, stopping at edges |
open arrows skip disabled options and stop at the edges without loop |
| K6 |
open, loop |
ArrowDown at end / ArrowUp at start |
wraps |
loop wraps arrow navigation |
| K7 |
open, single |
Enter / Space |
selects the highlighted option, closes, clears aria-activedescendant; change carries the key event |
open "Enter" selects the highlighted option and closes and open " " … |
| K8 |
open, multiple |
Enter |
toggles and stays open |
Enter in multiple mode toggles and keeps the popup open |
| K9 |
open / closed |
Escape |
closes without selecting (prevented); ignored while closed so ancestors can react |
Escape closes without selecting and is left alone while closed |
| K10 |
open |
Tab |
closes without preventing focus movement; commits only with selectOnTab |
Tab closes without committing unless selectOnTab is set |
| K11 |
open, single |
Alt+ArrowUp |
commits the highlighted option and closes |
Alt+ArrowUp commits the highlighted option and closes |
| K12 |
any |
IME composition / Ctrl/Meta chords |
ignored |
IME composition and modified keys are ignored |
| F1 |
named |
form submission |
hidden native select submits formValue(value) or the by key |
submits the serialized \by` key through a hidden native select` |
| F2 |
required, empty |
checkValidity() |
invalid; the invalid event focuses the trigger and marks aria-invalid/data-invalid |
empty single selections submit an empty value and fail required validation |
| F3 |
multiple |
form submission |
one selected native option per value |
multiple selections submit one entry per value with a custom serializer |
| F4 |
disabled |
form submission |
native control is disabled and excluded |
disabled selects are excluded from submission |
| F5 |
form attribute |
render outside the form |
associates with the owner form |
the form attribute associates a select rendered outside its form |
| F6 |
autofill / form reset |
native change / reset |
autofill maps back to items; reset restores defaultValue |
form reset restores the default selection |
| O1 |
open |
outside pointer-down |
dismissable layer closes the popup; the trigger is a branch and does not dismiss |
an outside pointer-down dismisses the popup through the dismissable layer, pointer-down on the trigger is a layer branch and does not dismiss |
| O2 |
open |
default portal |
listbox teleports to body, stays wired by id, and is removed on unmount |
the popup teleports to body by default and stays wired to the trigger |
| O3 |
item-aligned |
open |
selected (or first) option is centred over the trigger |
item-aligned mode publishes its strategy and centres the anchor option on the trigger |
| O4 |
scrollable viewport |
hover / leave scroll button |
buttons are hidden unless scrolling is possible and scroll by step |
scroll buttons appear only while the viewport can scroll and scroll on hover |
| V1 |
virtualized |
open / End / Home / PageDown / typeahead |
only a window renders; navigation and typeahead reach every option |
virtualized lists render a window and navigate across the whole collection |
| V2 |
virtualized, selected |
open |
scrolls to and highlights a selected option outside the window |
virtualized lists open on a selected option outside the initial window |
Options rendered by SelectVirtualizer carry an absolute index; the
collection orders options by index, so any option mixed into the same popup
(for example a pinned option before the virtual window) must pass index too.
SSR
Ids derive from the deterministic-id primitive. Closed and open trees, and a
virtual window laid out against initialViewportHeight, render byte-identical
markup across isolated requests and hydrate without warnings or node
replacement (renders byte-identical closed Select markup with SSR labels and form mirror, renders byte-identical open Select markup in place before the portal hydrates, hydrates closed and open Selects without mismatches,
server-renders and hydrates a deterministic virtual window). Pass itemText
so closed triggers render the right label before any option has mounted.
Scroll buttons measure after mount, so they render hidden in server markup.