---
title: Select
description: Displays a list of options for the user to pick one from, opened by a button, with groups, separators and keyboard navigation.
icon: chevron-down
author:
  name: shadcn
  url: https://github.com/shadcn
search:
  keywords: [dropdown, picker]
related:
  - /combobox
  - /multi-select
  - /dropdown-menu
mode: wide
---

<PoweredBy
  packages={[
    { name: 'Base UI', url: 'https://base-ui.com/react/components/select' },
    { name: 'Shadcn UI', url: 'https://ui.shadcn.com/docs/components/select' },
    { name: 'Lucide', url: 'https://lucide.dev/' },
  ]}
/>

### Installation

```package-install
npx shadcn@latest add @redpanda/select
```

<ComponentPreview name="select-demo" />

## When to use

Select components provide single-option selection from a list of options. Use this decision tree:

```mermaid
graph TD;
    A[❓ Need option selection?] --> B{"Many options (6+)?"};
    A --> C[ℹ️ Use other inputs];
    B --> D[✅ Use Select];
    B --> E{"Few options (2-5)?"};
    D --> F{Searchable needed?};
    E --> G[✅ Use Radio Group];
    F --> H[✅ Use Combobox];
    F --> I[✅ Use Select];

    classDef recommended fill:#4ade80,stroke:#22c55e,stroke-width:2px,color:#000;
    classDef discouraged fill:#fbbf24,stroke:#f59e0b,stroke-width:2px,color:#000;
    classDef decision fill:#60a5fa,stroke:#3b82f6,stroke-width:2px,color:#fff;
    classDef neutral fill:#9ca3af,stroke:#6b7280,stroke-width:2px,color:#fff;

    class D,G,H,I recommended;
    class A,B,E,F decision;
    class C neutral;
```

## Usage

```tsx
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
```

```tsx
<Select>
  <SelectTrigger className="w-[180px]">
    <SelectValue placeholder="Theme" />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="light">Light</SelectItem>
    <SelectItem value="dark">Dark</SelectItem>
    <SelectItem value="system">System</SelectItem>
  </SelectContent>
</Select>
```

## Anatomy

The Select component is built with Base UI primitives:

```
<Select> (Root Container)
├── <SelectTrigger> (Button)
│   ├── <SelectValue> (Placeholder/Selected)
│   └── <ChevronDown> Icon
├── <SelectContent> (Dropdown)
│   ├── Portal rendering
│   ├── Positioning system
│   └── <SelectItem> (Options)
│       ├── Checkmark indicator
│       └── Item text
└── Keyboard/Mouse handling
```

## Props

<div className="flex flex-row gap-x-3">
  <ExternalLink href="https://base-ui.com/react/components/select" text="Docs" />
  <ExternalLink href="https://base-ui.com/react/components/select#api-reference" text="API Reference" />
</div>

### Resolving labels in the trigger

`<SelectValue>` accepts three forms for the trigger label, in order of preference:

1. **No children** — the registry wrapper defers to Base UI's native resolution. Pass an `items` map on `<Select>` to
   look up the label, otherwise the raw value is stringified.
2. **Static children** — render the same node regardless of value (rarely useful for selects).
3. **Render-prop children** — `(value) => ReactNode`. Returning `null`/`undefined` falls back to `placeholder`. Use
   this for rich trigger content (icon + label, avatar + name, status dot + label) that mirrors the selected item.

```tsx
<Select items={{ '1': 'Any', '2': 'Read', '3': 'Write' }} value="1">
  <SelectTrigger>
    <SelectValue placeholder="Select operation" />
  </SelectTrigger>
  …
</Select>

// or, for rich content:
<SelectValue placeholder="Assign to teammate">
  {(value) => {
    const user = users.find((u) => u.value === value);
    return user ? <UserContent user={user} /> : null; // null → placeholder
  }}
</SelectValue>
```

## Examples

### Scrollable

<ComponentPreview name="select-scrollable-demo" description="A select component with a scrollable list of options." />

### Groups

<ComponentPreview
  name="select-groups-demo"
  description="Select with grouped options and group labels for categorization."
/>

### Disabled options

<ComponentPreview
  name="select-disabled-options-demo"
  description="Select with individual options disabled for permission-gated choices."
/>

### With description

<ComponentPreview
  name="select-with-description-demo"
  description="Select options rendered with primary label and secondary description."
/>

### With icon

<ComponentPreview name="select-with-icon-demo" description="Select with a leading icon rendered beside each option." />

### Status indicator

<ComponentPreview
  name="select-status-demo"
  description="Select options with leading colored status dots (running/paused/failed)."
/>

### Avatars

<ComponentPreview
  name="select-avatars-demo"
  description="Select with user avatars rendered per option for a people-picker pattern."
/>

### Custom rendering

<ComponentPreview
  name="select-custom-rendering-demo"
  description="Select with custom item rendering combining icon, label, and metadata."
/>

### Object values

<ComponentPreview
  name="select-object-values-demo"
  description="Select bound to object values with a render function exposing the full item."
/>

### Placeholder and clearable

<ComponentPreview
  name="select-placeholder-clearable-demo"
  description="Select with a placeholder and an explicit clearable option."
/>

### Error state

<ComponentPreview
  name="select-error-demo"
  description="Select in an error state with aria-invalid styling outside a form context."
/>

### Loading state

<ComponentPreview
  name="select-loading-demo"
  description="Select with a loading placeholder while options are fetched asynchronously."
/>

### Enum-backed value (label ≠ String(value))

:::info
Base UI's `<SelectValue>` renders the raw value when the popup hasn't been opened yet — fine for selects whose label
matches `String(value)`, broken for enum-backed selects (e.g. proto enums) where the trigger flashes `1` instead of
`Any` on first render. Two fixes, both shown in the demo: a render-prop on `<SelectValue>` (portable, no edge cases),
or an `items` map on `<Select>` (ergonomic when labels are static).
:::

<ComponentPreview
  name="select-enum-label-demo"
  description="Two patterns for showing the correct label before the popup mounts: render-prop on SelectValue, and items map on Select root."
/>

### Form

<ComponentPreview name="select-form-demo" description="A select component used in a form." />

:::info
In react-hook-form, drive `<Select>` with `value={field.value}` (controlled) and provide `defaultValues: { name: '' }`
on `useForm`. Using `defaultValue={field.value}` triggers Base UI's "uncontrolled default mutated after init" warning
the moment RHF rehydrates the field.
:::

## Keyboard interactions

| Key                                                | Action                                                                            |
| -------------------------------------------------- | --------------------------------------------------------------------------------- |
| `Enter` / `Space` / `ArrowDown` on `SelectTrigger` | Opens the listbox and focuses the currently selected item (or the first if none). |
| `ArrowUp` / `ArrowDown`                            | Moves between options.                                                            |
| `Home` / `End`                                     | Jumps to first / last option.                                                     |
| `Enter`                                            | Selects the focused option and closes the listbox.                                |
| `Escape`                                           | Closes without changing selection.                                                |
| Typing a character                                 | Typeahead jumps to the next option whose label starts with that character.        |

## Accessibility

- `SelectTrigger` is a real button with `aria-haspopup="listbox"` and `aria-expanded`; the listbox carries
  `role="listbox"` and each item `role="option"` with `aria-selected`.
- Provide a visible label via `<Label htmlFor>` or wrap the select in a `<Field>` so the selected value has a
  programmatic name.
- `onValueChange` is typed `(value: string, eventDetails) => void` — Base UI's two-argument signature with `value`
  narrowed to `string`.
- For large option lists (>20), prefer `Combobox` so users can type-ahead-filter. Select's typeahead only jumps to
  matching first letters.

## Credits

- We use [Base UI](https://base-ui.com/react/components/select) for the select component.
- We take our inspiration from [Shadcn UI](https://ui.shadcn.com/docs/components/select) for the select style.
