Pick one of a small, fixed set — content status, currency, department, sort order. The trigger shares its box with Input (controlVariants); the list supports groups, icons and descriptions, a clearable value and locale-aware typeahead. NativeSelect is the styled platform <select> for mobile-heavy website forms.
Only published posts appear on the website.
Installation
The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";
Usage
import { Field, Select, NativeSelect } from "@vamos-ui/react";
<Field label="Status">
<Select name="status" defaultValue="draft" items={[
{ value: "draft", label: "Draft", description: "Only editors can see it" },
{ value: "published", label: "Published" },
]} />
</Field>
<NativeSelect name="country" defaultValue="" placeholder="Choose a country" items={countries} />Use Select for up to ~15 options. For longer or searchable lists use Combobox.
Typeahead compares locale-folded text, so typing “i” finds “İstanbul” and “c” finds “Çanakkale”.
The selected item is marked with a check in the accent ink colour; highlight uses the neutral hover tone, readable on glass.
Examples
Groups
Give items a group and they render under headings with separators, in first-seen order. Disabled items are skipped by the keyboard.
Icons and descriptions
The description only shows in the open list; the trigger keeps the icon and label.
Clearable filters (CRM)
variant="pill" for filter bars. clearable adds a × button; clearing yields null (Radix forbids an empty item value).
Filter: owner = daniel, stage = any
Sizes & states
sm / md / lg follow the density axis. Error from Field, loading options, read-only (doesn't open, no chevron), disabled, and ghost for table cells.
Choose the currency the client pays in.
Set by the workspace owner.
Composite parts
SelectRoot + SelectTrigger + SelectContent + SelectGroup/SelectItem/SelectSeparator when you need full control over the markup.
NativeSelect (booking form)
The real <select>: the phone's own picker, no JavaScript to open, native value/onChange(event). Use it on public website forms.
Native form submit (HR)
With name, Radix renders a hidden native select, so FormData and Server Actions work without a form library.
API
Select
| Prop | Type | Default |
|---|---|---|
valueSelected value (controlled). null = nothing selected. | V | null | — |
defaultValueInitial value (uncontrolled). | V | null | null |
onValueChangeCalled with the new value, or null after clearing. | (value: V | null) => void | — |
items{ value, label, icon?, description?, disabled?, group? }. Or compose SelectItem children. | SelectOption<V>[] | — |
placeholderShown while nothing is selected (dictionary select.placeholder). | string | "Select…" |
sizeHeight from --control-h, same as Input. | "sm" | "md" | "lg" | "md" |
variantField look · borderless for table cells · rounded for filter bars. | "default" | "ghost" | "pill" | "default" |
clearableShow a × button while there is a value. | boolean | false |
loadingSpinner in the trigger and a status row in the list. | boolean | false |
readOnlyFocusable and announced (aria-readonly) but doesn't open. | boolean | false |
invalidDanger border + aria-invalid. Set by Field error. | boolean | — |
renderValueCustom trigger content for the selected item (items shorthand). | (item: SelectOption<V>) => ReactNode | — |
name / required / disabledForm submission and native semantics; also inherited from Field. | string / boolean | — |
open / defaultOpen / onOpenChangeControl the list. | boolean / (open) => void | — |
className / triggerClassName / contentClassNameClasses for the trigger and the floating list. | string | — |
labelsplaceholder, clear, loading, empty. | Partial<SelectLabels> | — |
SelectTrigger
| Prop | Type | Default |
|---|---|---|
size / variantBox look from controlVariants. | as Select | — |
invalid / readOnly / loading / requiredSame states as the shorthand. | boolean | — |
SelectItem
| Prop | Type | Default |
|---|---|---|
valueRequired, non-empty. | string | — |
iconLeading icon (also shown in the trigger). | ReactNode | — |
descriptionSecond line, list only. | ReactNode | — |
textValueTypeahead text when children aren't a string. Locale-folded automatically. | string | — |
disabledSkipped by keyboard and pointer. | boolean | — |
SelectContent / SelectGroup
| Prop | Type | Default |
|---|---|---|
itemSizeSelectContent: item density (follows the trigger by default). | "sm" | "md" | — |
positionSelectContent: popper sits under the trigger and is at least as wide. | "popper" | "item-aligned" | "popper" |
labelSelectGroup: heading; the group is announced with it. | ReactNode | — |
NativeSelect
| Prop | Type | Default |
|---|---|---|
value / defaultValue / onChangeThe native contract — onChange receives the event. | native | — |
itemsShorthand; groups become <optgroup>. Or pass <option> children. | { value, label, disabled?, group? }[] | — |
placeholderDisabled empty first option; use with defaultValue="" and required. | string | — |
size / variantSame box as Select and Input. | as Select | — |
invalid / readOnlyreadOnly blocks changes but stays focusable (aria-readonly). | boolean | — |
selectClassNameClasses for the <select>; className goes on the wrapper. | string | — |