Skip to content

Select

Picks one option from a list shown in a popover. Use with SelectItem children.

Inputv0.1.1experimentalWCAG 2.2 AAView spec
Country
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 select

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

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

Role

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

Department
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

Size sm
Size md
Size lg
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.

PropTypeDefault
labelrequired

Visible label.

stringnone
description

Help text shown under the trigger.

stringnone
errorMessage

Shown when the field is invalid.

stringnone
placeholder

Shown when nothing is selected.

string"Select an option"
size

Trigger height and font size.

"sm" | "md" | "lg""md"
selectedKey

Controlled selected key.

Key | nullnone
defaultSelectedKey

Initial key when uncontrolled.

Keynone
isDisabled

Prevents opening and dims the field.

booleanfalse
isInvalid

Marks the field as invalid and shows errorMessage.

booleanfalse
isRequired

Requires a selection for form submission.

booleanfalse
childrenrequired

SelectItem options.

nodenone
onSelectionChange

Called with the newly selected key.

(key: Key | null) => voidnone

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