The browser's own select menu, dressed like the other fields. It gives the native picker on phones and works with plain form posts. For custom option rows or search, use Select or Combobox.
Used for tax and shipping.
import { NativeSelect } from "@rdloom/react";
export default function NativeSelectBasicExample() {
return (
<div className="flex w-full justify-center">
<div className="w-full max-w-xs">
<NativeSelect
label="Country"
name="country"
placeholder="Choose a country"
description="Used for tax and shipping."
options={[
{ value: "ca", label: "Canada" },
{ value: "de", label: "Germany" },
{ value: "in", label: "India" },
{ value: "us", label: "United States" },
]}
/>
</div>
</div>
);
}Installation
npx rdloom add native-selectCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/native-select.jsonWorks, but without upgrade tracking.
Usage
import { NativeSelect } from "@rdloom/react";
<div className="flex w-full justify-center">
<div className="w-full max-w-xs">
<NativeSelect
label="Country"
name="country"
placeholder="Choose a country"
description="Used for tax and shipping."
options={[
{ value: "ca", label: "Canada" },
{ value: "de", label: "Germany" },
{ value: "in", label: "India" },
{ value: "us", label: "United States" },
]}
/>
</div>
</div>With groups
import { NativeSelect } from "@rdloom/react";
export default function NativeSelectWithGroupsExample() {
return (
<div className="flex w-full justify-center">
<div className="w-full max-w-xs">
<NativeSelect label="Time zone" name="timezone" defaultValue="europe-berlin">
<optgroup label="Europe">
<option value="europe-london">London</option>
<option value="europe-berlin">Berlin</option>
</optgroup>
<optgroup label="Asia">
<option value="asia-kolkata">Kolkata</option>
<option value="asia-tokyo">Tokyo</option>
</optgroup>
</NativeSelect>
</div>
</div>
);
}Invalid
Choose a plan to continue.
import { NativeSelect } from "@rdloom/react";
export default function NativeSelectInvalidExample() {
return (
<div className="flex w-full justify-center">
<div className="w-full max-w-xs">
<NativeSelect
label="Billing plan"
placeholder="Choose a plan"
isRequired
isInvalid
errorMessage="Choose a plan to continue."
options={[
{ value: "starter", label: "Starter" },
{ value: "team", label: "Team" },
]}
/>
</div>
</div>
);
}Disabled
Fixed after the workspace is created.
import { NativeSelect } from "@rdloom/react";
export default function NativeSelectDisabledExample() {
return (
<div className="flex w-full justify-center">
<div className="w-full max-w-xs">
<NativeSelect
label="Region"
isDisabled
defaultValue="eu"
description="Fixed after the workspace is created."
options={[
{ value: "eu", label: "Europe" },
{ value: "na", label: "North America" },
]}
/>
</div>
</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 field. | string | none |
errorMessageShown when the field is invalid. | string | none |
placeholderText of a disabled first option shown while nothing is chosen. | string | none |
sizeField height and font size. | "sm" | "md" | "lg" | "md" |
optionsOptions as data. A group has a label and its own options. Use children instead for hand-written option and optgroup elements. | Array<{ value: string; label: string; disabled?: boolean } | { label: string; options: Array<{ value: string; label: string; disabled?: boolean }> }> | none |
nameForm field name. | string | none |
valueControlled value. | string | none |
defaultValueInitial value when uncontrolled. | string | none |
onChangeCalled when the choice changes. | (event: ChangeEvent<HTMLSelectElement>) => void | none |
isDisabledPrevents changing the value and dims the field. | boolean | false |
isInvalidMarks the field as invalid and shows errorMessage. | boolean | false |
isRequiredRequires a choice for form submission. | boolean | false |
childrenoption and optgroup elements, used when options is not given. | node | none |
Accessibility
Role combobox, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Tab moves to the field
- Arrow keys change or browse the options, depending on the browser
- Space, Enter or Alt+Arrow Down opens the native list
- Typing jumps to a matching option
Screen readers announce
- On focus: the label, the current value and the kind of control
- The description and error text are read after the label
What your code must do
- The label is a real label element linked to the select
- Description and error are linked with aria-describedby
- An invalid field sets aria-invalid
- The chevron is decorative and hidden from assistive technology
Guidelines
Use it when
- A plain list where the device's own picker is best, especially on phones
- A form that posts to the server without script
- Many options where search is not needed
Avoid it when
- Rich option rows with icons or descriptions: use Select
- Searchable or very long lists: use Combobox
- Few options that should be visible: use RadioGroup
Don't
- A placeholder that reads like a real value
- Using a select for yes or no
- Passing isInvalid for required checks: leave it unset so the browser and your validation decide, and pass it only for errors the app finds itself
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-surface-default--rd-color-border-default--rd-color-border-strong--rd-color-text-default--rd-color-text-muted--rd-color-feedback-danger--rd-color-focus-ring--rd-radius-control