Select
A dropdown to pick one option from a list. The list opens in a portal, so it is never clipped by a scrolling window body and stays above floating windows.
import { Select } from 'jarvis-react-ui';
Basic
Pass the options as options, each with a unique value and a label. placeholder is shown
while nothing is selected; disabled options can't be picked.
import { Select } from 'jarvis-react-ui';
export default function Basic() {
return (
<Select
aria-label="Voice"
placeholder="Choose a voice…"
options={[
{ value: 'jarvis', label: 'JARVIS' },
{ value: 'friday', label: 'FRIDAY' },
{ value: 'edith', label: 'EDITH' },
{ value: 'karen', label: 'KAREN', disabled: true },
]}
/>
);
}
Controlled, with icons
Pass value and update it in onValueChange. icon shows content before the label, in the list
and in the trigger.
import { useState } from 'react';
import { Globe, Moon, Sun } from 'lucide-react';
import { Select } from 'jarvis-react-ui';
export default function Controlled() {
const [theme, setTheme] = useState('night');
return (
<div className="flex flex-col gap-3">
<Select
aria-label="Display mode"
value={theme}
onValueChange={setTheme}
options={[
{ value: 'night', label: 'Night', icon: <Moon size={12} /> },
{ value: 'day', label: 'Day', icon: <Sun size={12} /> },
{ value: 'auto', label: 'Follow location', icon: <Globe size={12} /> },
]}
/>
<span className="text-[10px] text-text-secondary">Value: {theme}</span>
</div>
);
}
States
size="sm" for dense layouts, invalid for validation errors, disabled to lock it. With name
the value is submitted with a surrounding <form>.
import { Select } from 'jarvis-react-ui';
const OPTIONS = [
{ value: 'a', label: 'Alpha' },
{ value: 'b', label: 'Bravo' },
];
export default function States() {
return (
<div className="flex flex-col gap-3">
<Select aria-label="Small" size="sm" options={OPTIONS} defaultValue="a" />
<Select aria-label="Invalid" invalid options={OPTIONS} />
<Select aria-label="Disabled" disabled options={OPTIONS} defaultValue="b" />
</div>
);
}
Sound
Opening plays menu_open, closing menu_close and picking an option click; hovering the trigger
plays hover_button. See Sound effects.
Accessibility
- The trigger is a button with
role="combobox",aria-haspopup="listbox",aria-expandedandaria-controls; name it witharia-label,aria-labelledbyor a<label htmlFor={id}>. - ArrowDown, ArrowUp, Enter and Space open the list. In the list, the arrows move the active option
(
aria-activedescendant) and skip disabled ones, Home and End jump to the ends, typing letters jumps to a matching label, Enter or Space picks it, and Escape closes the list and returns focus to the trigger. Escape does not close a surrounding floating window. - A pointer down outside the list closes it.
API
| Prop | Type | Default | Description |
|---|---|---|---|
options (required) |
SelectOption[] |
Options in display order. | |
aria-label |
string |
Accessible name of the trigger. Use it or aria-labelledby. |
|
aria-labelledby |
string |
Id of an element that names the select. | |
className |
string |
Additional class names for the trigger button. | |
defaultValue |
string |
Initially selected value when uncontrolled. | |
disabled |
boolean |
false |
Disables the select. |
fullWidth |
boolean |
false |
Stretches the trigger to the width of its container. |
id |
string |
Id of the trigger button, e.g. for a <label htmlFor>. |
|
invalid |
boolean |
false |
Marks the field as invalid: error border and aria-invalid. |
name |
string |
Name for a hidden input, so the value is submitted with a surrounding <form>. |
|
onValueChange |
((value: string) => void) |
Called with the new value when the user picks an option. | |
placeholder |
string |
Select… |
Text shown in the trigger while nothing is selected. |
size |
'sm' | 'md' |
'md' |
Height and font size of the trigger. |
value |
string |
Controlled selected value. Leave undefined to use defaultValue. |
Type SelectOption
One option of a Select.
| Field | Type | Default | Description |
|---|---|---|---|
label (required) |
string |
Text shown in the list and, when selected, in the trigger. Also used for typeahead. | |
value (required) |
string |
Unique value of the option, reported through onValueChange. |
|
disabled |
boolean |
false |
Disables the option. |
icon |
ReactNode |
Icon or other content shown before the label. |