Skip to content

Native Select

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.

Inputv0.1.0experimentalWCAG 2.2 AAView spec

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-select

Copies 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.json

Works, 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.

PropTypeDefault
labelrequired

Visible label.

stringnone
description

Help text shown under the field.

stringnone
errorMessage

Shown when the field is invalid.

stringnone
placeholder

Text of a disabled first option shown while nothing is chosen.

stringnone
size

Field height and font size.

"sm" | "md" | "lg""md"
options

Options 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
name

Form field name.

stringnone
value

Controlled value.

stringnone
defaultValue

Initial value when uncontrolled.

stringnone
onChange

Called when the choice changes.

(event: ChangeEvent<HTMLSelectElement>) => voidnone
isDisabled

Prevents changing the value and dims the field.

booleanfalse
isInvalid

Marks the field as invalid and shows errorMessage.

booleanfalse
isRequired

Requires a choice for form submission.

booleanfalse
children

option and optgroup elements, used when options is not given.

nodenone

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