Picks one option from a list shown in a popover. Use with SelectItem children.
import { Select, SelectItem } from "@rdloom/react";
export default function SelectBasicExample() {
return (
<Select className="w-64" label="Country" placeholder="Choose a country">
<SelectItem id="in">India</SelectItem>
<SelectItem id="us">United States</SelectItem>
<SelectItem id="de">Germany</SelectItem>
<SelectItem id="jp">Japan</SelectItem>
</Select>
);
}Installation
npx rdloom add selectCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/select.jsonWorks, but without upgrade tracking.
Usage
import { Select, SelectItem } from "@rdloom/react";
<Select className="w-64" label="Country" placeholder="Choose a country">
<SelectItem id="in">India</SelectItem>
<SelectItem id="us">United States</SelectItem>
<SelectItem id="de">Germany</SelectItem>
<SelectItem id="jp">Japan</SelectItem>
</Select>Controlled
Selected: editor
import { useState } from "react";
import { Select, SelectItem } from "@rdloom/react";
export default function SelectControlledExample() {
const [role, setRole] = useState("editor");
return (
<div className="flex flex-col gap-2">
<Select className="w-64" label="Role" selectedKey={role} onSelectionChange={(key) => setRole(key as string)}>
<SelectItem id="admin">Admin</SelectItem>
<SelectItem id="editor">Editor</SelectItem>
<SelectItem id="viewer">Viewer</SelectItem>
</Select>
<p className="text-sm">Selected: {role}</p>
</div>
);
}Invalid
import { Button, Select, SelectItem } from "@rdloom/react";
export default function SelectInvalidExample() {
return (
<form className="flex flex-col items-start gap-3" onSubmit={(e) => e.preventDefault()}>
{/* Submit without choosing to see the error. */}
<Select className="w-64" label="Department" isRequired>
<SelectItem id="eng">Engineering</SelectItem>
<SelectItem id="sales">Sales</SelectItem>
</Select>
<Button type="submit">Save</Button>
</form>
);
}Sizes
import { Select, SelectItem } from "@rdloom/react";
export default function SelectSizesExample() {
return (
<div className="flex flex-wrap items-end gap-3">
{(["sm", "md", "lg"] as const).map((size) => (
<Select key={size} className="w-40" label={`Size ${size}`} size={size} defaultSelectedKey="a">
<SelectItem id="a">Option A</SelectItem>
<SelectItem id="b">Option B</SelectItem>
</Select>
))}
</div>
);
}API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
labelrequiredVisible label. | string | none |
descriptionHelp text shown under the trigger. | string | none |
errorMessageShown when the field is invalid. | string | none |
placeholderShown when nothing is selected. | string | "Select an option" |
sizeTrigger height and font size. | "sm" | "md" | "lg" | "md" |
selectedKeyControlled selected key. | Key | null | none |
defaultSelectedKeyInitial key when uncontrolled. | Key | none |
isDisabledPrevents opening and dims the field. | boolean | false |
isInvalidMarks the field as invalid and shows errorMessage. | boolean | false |
isRequiredRequires a selection for form submission. | boolean | false |
childrenrequiredSelectItem options. | node | none |
onSelectionChangeCalled with the newly selected key. | (key: Key | null) => void | none |
Accessibility
Role button, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Enter, Space or Arrow Down opens the list
- Arrow keys move through options
- Typing jumps to matching options
- Esc closes without changing the value
Screen readers announce
- On focus: the label, the current value and that it opens a list (pop-up button, collapsed)
- Opening announces the list and the focused option with its position
- After choosing, the new value is announced and focus is back on the field
What your code must do
- Label is linked to the trigger
- Selected value is announced
Guidelines
Use it when
- Choosing one of roughly 5 to 15 known options
Avoid it when
- Few options that should be visible: use RadioGroup
- Long or searchable lists: use Combobox
Don't
- Using a Select for yes/no
- Placeholder that looks like a real value
- Passing isInvalid for required or format checks (even isInvalid={false}): any defined value takes over validity and hides the built-in errors. Use isRequired and validate; pass isInvalid only for errors the app finds itself, such as from the server
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-surface-default--rd-color-surface-raised--rd-color-surface-subtle--rd-color-border-default--rd-color-border-strong--rd-color-text-default--rd-color-text-muted--rd-color-action-primary--rd-color-feedback-danger--rd-color-focus-ring--rd-radius-control--rd-radius-overlay