Select

Atom
A single-choice dropdown field: a trigger showing the chosen value that opens a panel of options, styled to match Canvas inputs.

Playground

Every control sets a real prop of SelectProps. The JSX below updates as you go.
Role
Props
label
string
value
string
placeholder
string
open
boolean
Size
pick one
required
boolean
disabled
boolean
JSX · SelectProps
<Select label="Role" value="Design engineer" placeholder="Choose a role" />

Variants

9 examples from src/atoms/select/select.md, each rendered live from its own JSX.
Role
Dense field
<Select dense eyebrow soft plainOptions steadyPress menuWidth={260} label="Role" defaultValue="Developer" options={["Architect", "Developer", "Reviewer"]} />
Inline label
<Select inline label="Rows" defaultValue="10" options={["10", "25", "50"]} />
Country
Required field
<Select label="Country" required defaultValue="United States" options={["United States", "Canada", "Mexico"]} />
Country
Small
<Select small label="Country" defaultValue="United States" options={["United States", "Canada", "Mexico"]} />
Country
Large
<Select large label="Country" defaultValue="United States" options={["United States", "Canada", "Mexico"]} />
Country
Country
<Select
label="Country"
defaultValue="US"
options={[
{ value: "US", label: "United States", leading: "🇺🇸" },
{ value: "CA", label: "Canada", leading: "🇨🇦" },
{ value: "GB", label: "United Kingdom", leading: "🇬🇧" },
{ value: "DE", label: "Germany", leading: "🇩🇪" },
{ value: "JP", label: "Japan", leading: "🇯🇵" },
]}
/>
Country
With leading icon
<Select icon label="Country" defaultValue="United States" options={["United States", "Canada", "Mexico"]} />
Country
Disabled
<Select disabled label="Country" defaultValue="United States" options={["United States", "Canada", "Mexico"]} />
Measure
<Column snug>
<Select xs start options={["Small", "Medium", "Large"]} placeholder="xs step, pinned to the start (320)" />
<Select lg start options={["Small", "Medium", "Large"]} placeholder="lg step, pinned to the start (512)" />
<Container lg start><Select options={["Small", "Medium", "Large"]} placeholder="Bare, in an lg Container: fills it (512)" /></Container>
</Column>

Sizes

Role
small
Role
default
Role
large

States

Hover, focus and press are pinned so they can be compared side by side; the rest are real props.
Role
default
Role
hover
Role
focus
Role
open
Role
disabled

Anatomy

Role
1
Label
2
Trigger
3
Value
4
Chevron
5
Menu
Dimmed parts appear only in some states or variants.

Props

26 props on SelectProps, grouped as they are declared in source. Required props are marked *.
Prop
Type
Default
Description
Props
23 props
value
string
–
Controlled selection; omit for uncontrolled use. Empty shows the placeholder. With plain string options this is the option itself; with SelectOption objects it is the option's value, and the trigger shows its label.
defaultValue
string
–
Initial selection for uncontrolled use (a bare <Select options/> picks on its own).
options
Array<string | SelectOption>
–
The selectable options: bare strings, or { value, label } objects when the stored value differs from the text shown for it.
label
string
–
The field's persistent label. Its placement is platform-adaptive: iOS and web render it ABOVE the trigger; Android renders the Material 3 in-container FLOATING label (centered like a placeholder at rest, floating to the top once the menu opens or a value is selected). The label names the field for a11y.
accessibilityLabel
string
–
Accessible purpose for the trigger and option list. Overrides the visible label for assistive technology; otherwise the label or placeholder names them.
inline
boolean
false
Places the label as a LEADING cluster INSIDE the trigger row (a toolbar-style labeled select) instead of the persistent above/floating placement. Takes effect only alongside label. The inline label still names the trigger (accessibilityLabel / aria-labelledby) and shares its tap target, since it lives inside the trigger Pressable. Pairs with fit for a content-hugging toolbar cluster.
required
boolean
false
Marks the field as required and announces "required" with the trigger name. A visible label also gets a decorative destructive "*".
icon
boolean
false
Renders a leading globe glyph inside the trigger, indented so the value clears it.
placeholder
string
"Select an option"
Prompt shown in the trigger when no value is selected.
open
boolean
false
Controlled open state of the option list; omit for uncontrolled use.
defaultOpen
boolean
false
Initial open state for uncontrolled use.
onOpenChange
(open: boolean) => void
–
Fired when the open state changes (trigger press, select), in both modes.
disabled
boolean
false
Dims the control and blocks interaction.
onSelect
(option: string) => void
–
Called with the chosen option's value when a row is pressed (both modes).
testID
string
–
E2E hook forwarded to the trigger pressable.
dense
boolean
false
Use the compact content-sized web field with a drawn disclosure chevron.
eyebrow
boolean
false
Use the compact application eyebrow above a web field.
soft
boolean
false
Use the brand field fill instead of the card fill.
plainOptions
boolean
false
Text-only options: selected ink and a pointer wash, without a check gutter.
steadyPress
boolean
false
Keep the resting fill and opacity during a press.
combobox
boolean
false
Announce a selection field as a combobox rather than a menu button.
menuWidth
number
–
Width of the floating menu, clamped within the overlay outlet.
ref
React.Ref<View>
–
Ref to the interactive trigger, preserving overlay measurement. Typed as a React Native View. On web, React Native Web exposes its DOM host; focus() and blur() move browser focus. Native host behavior depends on the platform and React Native version. Calling focus() does not activate the control or call accessibility focus APIs.
Size
pick one
3 props
small
boolean
false
large
boolean
false
style
LayoutStyle
–
Composition within a parent only, never a restyle hook and never a width: the parent layout container provides the bounds.
MeasureProps · width steps xxxs 192 · xxs 256 · xs 320 · sm 384 · md 448 · lg 512 · xl 576 · xxl 672 · xxxl 768 · wide 896 · wider 1024 · widest 1152 · page 1280, plus start to pin to the leading edge.

Accessibility and keyboard

Keyboard
ArrowDown
Opens the list
ArrowUp
ArrowDown
Moves through options
Enter
Commits the active option
Escape
Closes the list, keeps the text
Semantics
Roles
button
combobox
option
States
disabled
expanded
selected
ARIA on web
aria-controls
aria-disabled
aria-expanded
aria-haspopup
aria-hidden
aria-label
aria-labelledby
aria-required
aria-selected
Below the platform minimum, the touch area grows to 44 pt on iOS and 48 dp on Android without moving the layout.
From the docs
Pass label (and required) to name the field: iOS and web render the label above the trigger, while Android floats the Material 3 in-container label once the menu opens or a value is selected. On Android the trigger grows with the user's text size, and a label longer than the trigger truncates on one line clear of the chevron while the trigger's accessible name stays the whole label. The trigger fills the parent it is given; a step of its own (xs, lg, …, with start to pin it to the leading edge) or a Conta
The label also names the option list. Use accessibilityLabel to provide an explicit purpose when the visible label or placeholder is insufficient; it overrides both accessible names. A required field announces "required" with the button name and marks the option list as required. The selected value never replaces the field's purpose.
Pass ref to access the interactive trigger, preserving overlay measurement. Use useRef<ComponentRef<typeof Select>>(null) from React, or useRef<View>(null) with React Native's View type. Object and callback refs are supported and detach on unmount. Calling ref.current?.focus() or .blur() delegates to the host without activating the control. Browser focus is supported; native focus depends on the platform and React Native version, and is separate from accessibility focus.
Use dense eyebrow soft for the compact application field on web: a 32-pixel trigger with a 600 13-pixel value under a tracked uppercase eyebrow label at the kit’s 10-pixel reading floor, and 30-pixel rows. plainOptions removes the check and marks the chosen row with its wash alone; pointer hover uses the brand wash. steadyPress keeps the resting fill and opacity while pressed. menuWidth bounds the floating menu independently of the field. combobox exposes the selection-field role. Native skins keep their

Platform skins

The playground's props on each platform: same tree, native shapes, type and touch sizes.
Web
control 10 · field 10 · card 16 · Instrument Sans
iOS
capsule controls · field 8 · card 12 · SF Pro · 44 pt
Android
Material 3 stadium · field 4 · card 12 · Roboto · 48 dp

Usage

<Select label="Country" defaultValue="United States" options={["United States", "Canada", "Mexico"]} />
Pass label (and required) to name the field: iOS and web render the label above the trigger, while Android floats the Material 3 in-container label once the menu opens or a value is selected. On Android the trigger grows with the user's text size, and a label longer than the trigger truncates on one line clear of the chevron while the trigger's accessible name stays the whole label. The trigger fills the parent it is given; a step of its own (xs, lg, …, with start to pin it to the leading edge) or a Container step sets its measure.
In glass mode the option panel is a dense-layer glass card under the trigger (or above it when it fits there); the trigger stays in place with its value and chevron, and the chosen value and expanded state commit immediately. Solid mode paints the skin's own panel.
The label also names the option list. Use accessibilityLabel to provide an explicit purpose when the visible label or placeholder is insufficient; it overrides both accessible names. A required field announces "required" with the button name and marks the option list as required. The selected value never replaces the field's purpose.

Tokens

Read from the playground render. Values follow the scheme and the ion hue.
--bg2
--fd
--ft
--ln2
--tx
--tx2
--tx3

Related components