ComponentsForms & inputs

Select

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.

Radix Select components/select.tsx, components/native-select.tsx, lib/variants.ts, lib/text.tstheme · material · accent · density · font

Only published posts appear on the website.

Installation

$ pnpm add @vamos-ui/react

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

PropTypeDefault
value
Selected value (controlled). null = nothing selected.
V | null—
defaultValue
Initial value (uncontrolled).
V | nullnull
onValueChange
Called 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>[]—
placeholder
Shown while nothing is selected (dictionary select.placeholder).
string"Select…"
size
Height from --control-h, same as Input.
"sm" | "md" | "lg""md"
variant
Field look · borderless for table cells · rounded for filter bars.
"default" | "ghost" | "pill""default"
clearable
Show a × button while there is a value.
booleanfalse
loading
Spinner in the trigger and a status row in the list.
booleanfalse
readOnly
Focusable and announced (aria-readonly) but doesn't open.
booleanfalse
invalid
Danger border + aria-invalid. Set by Field error.
boolean—
renderValue
Custom trigger content for the selected item (items shorthand).
(item: SelectOption<V>) => ReactNode—
name / required / disabled
Form submission and native semantics; also inherited from Field.
string / boolean—
open / defaultOpen / onOpenChange
Control the list.
boolean / (open) => void—
className / triggerClassName / contentClassName
Classes for the trigger and the floating list.
string—
labels
placeholder, clear, loading, empty.
Partial<SelectLabels>—

SelectTrigger

PropTypeDefault
size / variant
Box look from controlVariants.
as Select—
invalid / readOnly / loading / required
Same states as the shorthand.
boolean—

SelectItem

PropTypeDefault
value
Required, non-empty.
string—
icon
Leading icon (also shown in the trigger).
ReactNode—
description
Second line, list only.
ReactNode—
textValue
Typeahead text when children aren't a string. Locale-folded automatically.
string—
disabled
Skipped by keyboard and pointer.
boolean—

SelectContent / SelectGroup

PropTypeDefault
itemSize
SelectContent: item density (follows the trigger by default).
"sm" | "md"—
position
SelectContent: popper sits under the trigger and is at least as wide.
"popper" | "item-aligned""popper"
label
SelectGroup: heading; the group is announced with it.
ReactNode—

NativeSelect

PropTypeDefault
value / defaultValue / onChange
The native contract — onChange receives the event.
native—
items
Shorthand; groups become <optgroup>. Or pass <option> children.
{ value, label, disabled?, group? }[]—
placeholder
Disabled empty first option; use with defaultValue="" and required.
string—
size / variant
Same box as Select and Input.
as Select—
invalid / readOnly
readOnly blocks changes but stays focusable (aria-readonly).
boolean—
selectClassName
Classes for the <select>; className goes on the wrapper.
string—