Native Select
Accessible, unstyled native select with controlled and uncontrolled selection.
| Package | @vizejs/ui/native-select |
| Maturity | stable |
| Own the source | vize lib pull native-select |
| Requires | controllable-state, id |
| Aliases | native select, select, form select, html select |
| Covers | HTML select element, React Aria Select native escape hatch, Radix Select native form fallback, Reka UI Select hidden native select interop |
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 { NativeSelect } from "@vizejs/ui/native-select";
Or copy the source into your project with vize lib pull native-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
<!-- Native select bound with v-model, rendering options from data with one disabled choice. -->
<script setup lang="ts">
import { ref, useId } from "vue";
import { NativeSelect, type NativeSelectOption, type NativeSelectValue } from "@vizejs/ui/native-select";
const plan = ref<NativeSelectValue>("team");
const selectId = useId();
const plans: readonly NativeSelectOption[] = [
{ value: "starter", label: "Starter — free" },
{ value: "team", label: "Team — $12 per seat" },
{ value: "enterprise", label: "Enterprise — contact sales", disabled: true },
];
</script>
<template>
<div>
<label :for="selectId">Billing plan</label>
<NativeSelect :id="selectId" v-model="plan" name="plan" :options="plans" />
<output :for="selectId">Selected plan: {{ plan }}</output>
</div>
</template>
API
NativeSelect
Source: src/families/selection/native-select/native-select.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string | null |
undefined |
Consumer-owned select id. null and undefined select a deterministic fallback. |
name |
string |
undefined |
Native form field name. |
modelValue |
NativeSelectValue |
undefined |
Controlled selected value. undefined selects uncontrolled behavior. |
defaultValue |
NativeSelectValue |
undefined |
Initial value for uncontrolled use and the value restored by form reset. |
options |
readonly NativeSelectOption[] |
[] |
Flat option descriptors rendered before the default slot. |
multiple |
boolean |
false |
Use the native multiple-selection mode. |
size |
number |
undefined |
Native visible row count. Values below one are ignored by the browser. |
disabled |
boolean |
false |
Disable focus, selection, and native form submission. |
required |
boolean |
false |
Mark the native select as required for constraint validation. |
direction |
NativeSelectDirection |
"ltr" |
Reading direction reflected with dir and data-direction. |
ariaLabel |
string |
undefined |
Accessible name when no label or aria-labelledby supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the select. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the select. |
ariaErrormessage |
string |
undefined |
Id of the validation error message used while invalid. |
ariaInvalid |
NativeSelectAriaInvalid |
false |
Invalid state announced to assistive technology. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue |
[value: NativeSelectValue] |
Fired when selection requests a new controlled value. |
change |
[value: NativeSelectValue, previous: NativeSelectValue, nativeEvent: Event] |
Fired after native change/commit with the next value, previous value, and native Event. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
NativeSelectSlotState |
Renders optional custom option content with the current NativeSelect state. |
Exposed
| Member | Type | Description |
|---|---|---|
value |
NativeSelectValue |
Current selected value, or an array when multiple is true. |
selectedValues |
readonly string[] |
Selected values as a stable readonly array. |
disabled |
boolean |
Whether the native select is disabled. |
required |
boolean |
Whether the native select participates in required validation. |
invalid |
boolean |
Whether the native select is currently marked invalid. |
selectionMode |
NativeSelectSelectionMode |
Current selection mode. |
multiple |
boolean |
Whether native multiple selection is enabled. |
direction |
NativeSelectDirection |
Reading direction reflected on the element. |
state |
NativeSelectState |
Stable state token for styling and tests. |
element |
HTMLSelectElement | null |
Rendered native select element. |
id |
string |
Root-owned id for the select. |
focus |
(options?: FocusOptions) => void |
Move DOM focus to the native select. |
setValue |
(value: NativeSelectValue) => boolean |
Request a selected value update and report whether it differs. |
clear |
() => boolean |
Clear the current selection and report whether it changed. |
reset |
() => boolean |
Restore the current default value and report whether it changed. |
Behavior
Contract
native-select.vue is a headless primitive over the platform <select> element. It
does not portal, render overlays, own typeahead, or replace browser selection
UI. Consumers style the native element and prop-rendered options through parts,
slots, CSS, and data attributes.
readOnly is intentionally unsupported because native <select> has no
readonly state. Consumers that need a non-editable submitted value should keep
the component controlled and ignore changes, or render disabled UI with a
separate hidden form value when that submission behavior is required.
Public Surface
| Surface | Contract |
|---|---|
NativeSelect props |
id, name, modelValue, defaultValue, options, multiple, size, disabled, required, direction, ariaLabel, ariaLabelledby, ariaDescribedby, ariaErrormessage, ariaInvalid |
| Emits | update:modelValue(value), change(value, previous, nativeEvent) |
| Slots | default(state) for consumer-owned native <option> and <optgroup> children |
| Expose | element, id, value, selectedValues, disabled, required, invalid, selectionMode, multiple, direction, state, focus, setValue, clear, reset |
| Parts | root, option for prop-rendered options |
| Root data attributes | data-vize-ui="native-select", data-state, data-disabled, data-required, data-invalid, data-selection-mode, data-selection-count, data-direction, data-value |
| Option data attrs | data-vize-ui="native-select-option", data-state, data-value, data-selected, data-disabled for prop-rendered options |
Normative Behavior
| Input | Single Selection | Multiple Selection |
|---|---|---|
| Tab | Moves focus to the native select when enabled. Disabled selects are skipped. | Same as single. |
| Native open/navigation | Delegated entirely to the browser and operating system. | Same as single, including platform modifier keys. |
| Controlled value | Emits the requested string and waits for the parent to accept it. | Emits a readonly string array in DOM option order and waits for parent. |
| Uncontrolled value | Mutates internal state, emits the requested string, and resets to default. | Mutates internal state, emits selected strings, and resets to default. |
options prop |
Renders flat native options before slotted children. Disabled options remain. | Same as single. |
| Default slot | Receives value, selected values, state, disabled, invalid, mode, and direction. | Same as single; consumers bind selected state on custom options. |
disabled |
Removes the select from focus order and native form submission. | Same as single. |
required/name |
Uses native constraint validation and form submission semantics. | Same as single with browser-defined multiple submission behavior. |
ariaInvalid |
Reflects aria-invalid and gates aria-errormessage. |
Same as single. |
readOnly |
Unsupported by native select and not exposed as a prop. | Same as single. |
SSR
Generated ids use the deterministic-id primitive. Isolated SSR requests must produce byte-identical markup for the same tree, including selected prop-rendered options, and hydration must not replace the rendered native select.