Select
Accessible dropdown for picking one value from a list. Built on React Aria with full keyboard navigation and ARIA listbox semantics.
Uses React Aria Select; role="listbox" popup with aria-selected items; full keyboard navigation and typeahead.
Preview
tsx
import { Select } from '@arshad-shah/cynosure-react';
import { useState } from 'react';
const fruits = [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
{ value: 'cherry', label: 'Cherry' },
{ value: 'date', label: 'Date' },
{ value: 'elderberry', label: 'Elderberry' },
];
export default function Example() {
const [value, setValue] = useState<string | null>(null);
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', width: '240px' }}>
<Select
aria-label="Favourite fruit"
items={fruits}
value={value}
onValueChange={setValue}
placeholder="Pick a fruit…"
/>
{value && (
<p style={{ fontSize: '0.875rem', color: 'var(--color-fg-muted, #6b7280)', margin: 0 }}>
Selected: {value}
</p>
)}
</div>
);
}
Rendered as a segmented control: the value and the chevron sit as raised tiles inside a tinted, padded track (the same container NumberInput and Input use). variant tints the track.
Grouped options
Section titled “Grouped options”Use the section field in the items array to automatically group items under section headers.
Preview
tsx
import { Select } from '@arshad-shah/cynosure-react';
const items = [
{ value: 'apple', label: 'Apple', section: 'Fruits' },
{ value: 'banana', label: 'Banana', section: 'Fruits' },
{ value: 'cherry', label: 'Cherry', section: 'Fruits' },
{ value: 'broccoli', label: 'Broccoli', section: 'Vegetables' },
{ value: 'carrot', label: 'Carrot', section: 'Vegetables' },
{ value: 'spinach', label: 'Spinach', section: 'Vegetables' },
];
export default function Example() {
return (
<div style={{ width: '240px' }}>
<Select aria-label="Food item" items={items} placeholder="Choose a food…" />
</div>
);
}
Disabled
Section titled “Disabled” Preview
tsx
import { Select } from '@arshad-shah/cynosure-react';
const options = [
{ value: 'a', label: 'Option A' },
{ value: 'b', label: 'Option B' },
];
export default function Example() {
return (
<div style={{ width: '240px' }}>
<Select aria-label="Disabled select" items={options} defaultValue="a" disabled />
</div>
);
}
Invalid state
Section titled “Invalid state” Preview
tsx
import { Select } from '@arshad-shah/cynosure-react';
import { useState } from 'react';
const options = [
{ value: 'daily', label: 'Daily' },
{ value: 'weekly', label: 'Weekly' },
{ value: 'monthly', label: 'Monthly' },
];
export default function Example() {
const [value, setValue] = useState<string | null>(null);
const invalid = value === null;
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', width: '240px' }}>
<Select
aria-label="Frequency"
items={options}
value={value}
onValueChange={setValue}
invalid={invalid}
placeholder="Select frequency…"
/>
{invalid && (
<p style={{ fontSize: '0.875rem', color: 'var(--color-danger-600, #dc2626)', margin: 0 }}>
Please select a frequency.
</p>
)}
</div>
);
}
PropTypeDefaultDescription
value
string|null
—
Currently selected value (controlled).
The current value (controlled).
defaultValue
string|null
—
Initial value (uncontrolled).
The default value (uncontrolled).
onValueChange
((value: T) => void)
—
Called when the selection changes.
placeholder
string
"Select…"
'Select an item' (localized)
Shown when nothing is selected.
Temporary text that occupies the select when it is empty.
label
string
—
Visible label (rendered as the trigger's accessible name when no children label is passed).
aria-label
string
—
Aria label when no visual label is available.
Defines a string value that labels the current element.
size
"sm"|"md"|"lg"
"md"
Control size.
variant
"outline"|"filled"|"ghost"
"outline"
Visual treatment matching the shared form-control vocabulary.
disabled
boolean
—
Disables interaction.
required
boolean
—
Marks the field as required for form submission.
invalid
boolean
—
Renders the invalid state and sets `aria-invalid`.
name
string
—
Submitted form field name.
The name of the input, used when submitting an HTML form.
id
string
—
Element id.
The element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id).
items
readonly SelectItemData<T>[]
—
Data-driven items — overrides `children`. Sections group by `section`.
children
ReactNode
—
JSX children (`<SelectItem>` / `<SelectSection>`). Ignored when `items` is passed.
className
string
—
style
CSSProperties
—
ref
Ref<HTMLButtonElement>
—
Accessibility
Section titled “Accessibility”- Built on React Aria — the popup uses
role="listbox"withrole="option"items andaria-selected. - Full keyboard navigation: Arrow keys move focus, Enter/Space selects, Escape closes.
- Typeahead: typing a character jumps to the first matching item.
Recipes
Section titled “Recipes”- Pass
itemswith asectionfield for automatic grouping — no JSX needed. - Use
onValueChangeto trigger async side effects when the user picks a value.