<!-- Generated by scripts/gen-docs.mts from the JSDoc in registry/. Do not edit; run `pnpm run docs`. -->

# Select

A labeled list of choices on the Field contract: pass `label`, `options` (or `SelectItem` children) and `error` from your server action or client check. With `multiple` the value is a `string[]`. On a phone the list opens as a bottom sheet you scroll with your thumb; elsewhere it opens next to the trigger, over the chosen option. It submits its value under `name` in a plain form post. Covers Catalyst's Listbox.

```tsx
import { Select } from "@/components/ouro/select/select";
```

## Example

```tsx
<Select
  label="Course"
  name="course"
  placeholder="Choose a course"
  options={[{ value: "starter", label: "Starter" }, { value: "main", label: "Main" }]}
  error={errors?.course}
/>
<Select label="Tags" name="tags" multiple defaultValue={["quick"]} options={tags} />
```

## Usage

A `multiple` Select posts one entry per chosen value, all under `name`, in the order they were chosen, and no entry when nothing is chosen: read them with `formData.getAll("tags")`, not `formData.get`. In a test, open the list and choose an option with real pointer events (`userEvent.click` in Vitest browser mode, `locator.click()` in Playwright): the options act on pointer events, so a synthetic `fireEvent.click` chooses nothing.

## Use instead when

- Use `NativeSelect` instead for the operating system's own picker (long lists of plain text on phones, such as countries), `RadioGroup` for two to five choices that should all show, and `Combobox` when the user needs to type to filter.

## Props

The props the modes share. Type: `SelectProps`.

| Prop | Type | Description |
| --- | --- | --- |
| `autoComplete` | `string` | Autofill hint for the hidden input that submits the value, such as `"country"`. |
| `children` | `ReactNode` | `SelectItem`s and `SelectGroup`s, when you do not pass `options`. |
| `className` | `string` | Layout only: margins, width, grid placement (`mt-4`, `col-span-2`). Appearance classes warn in development; style fields in your theme. |
| `description` | `ReactNode` | Supporting text under the label, read after the label through `aria-describedby`. |
| `disabled` | `boolean` | Disables the select and fades it. |
| `error` | `ReadonlyErrorMessages` | What is wrong, from your server action or client check. The message (or several, as a list) shows under the select, which gets `aria-invalid`. A message is text or a React node (one with a link); an empty string or array means valid. |
| `form` | `string` | The id of a form outside which the select sits, so its value submits with that form. |
| `hideLabel` | `boolean` | Hides the label visually; screen readers still read it as the select's name. |
| `label` (required) | `string` | What the select is for, shown above it and read as its name. Required: every control needs a name. To hide it visually, keep it and add `hideLabel`. On phones it also heads the list. |
| `name` | `string` | The form field name the value submits under. With `multiple`, each value submits under it. |
| `options` | `SelectOptions` | The choices: `[{ value: "main", label: "Main" }]`, and groups as `{ label: "Fruit", options: [...] }`. Pass `SelectItem` children instead when an option needs more than text. |
| `placeholder` | `string` | Shown in the trigger while nothing is chosen: `"Choose a course"`. It is not a label. |
| `readOnly` | `boolean` | The value can be seen but not changed. |
| `ref` | `Ref<HTMLButtonElement>` | The trigger button. |
| `required` | `boolean` | A choice is required: the form will not submit without one, and the label gets a marker. |

## Modes

`SelectProps` is a union: pass the props of exactly one mode. Mixing them is a type error.

### Multiple: `SelectMultipleProps`

A Select that picks several values, as a `string[]`.

| Prop | Type | Description |
| --- | --- | --- |
| `defaultValue` | `string[]` | The value chosen at first, when the select keeps its own state: a `string` or `null`; with `multiple`, a `string[]`. |
| `multiple` (required) | `true` | Pick several values: the list stays open while the user chooses, and the value is a `string[]` instead of `string \| null`. |
| `onValueChange` | `((value: string[]) => void)` | Called with the new value when the user chooses an option: a `string` or `null`; with `multiple`, every chosen value as a `string[]`. |
| `value` | `string[]` | The chosen value, when you control it: a `string`, or `null` for none; with `multiple`, a `string[]`. |

### Single: `SelectSingleProps`

A Select that picks one value: a `string`, or `null` while nothing is chosen.

| Prop | Type | Description |
| --- | --- | --- |
| `defaultValue` | `string \| null` | The value chosen at first, when the select keeps its own state: a `string` or `null`; with `multiple`, a `string[]`. |
| `multiple` | `false` | Pick several values: the list stays open while the user chooses, and the value is a `string[]` instead of `string \| null`. |
| `onValueChange` | `((value: string \| null) => void)` | Called with the new value when the user chooses an option: a `string` or `null`; with `multiple`, every chosen value as a `string[]`. |
| `value` | `string \| null` | The chosen value, when you control it: a `string`, or `null` for none; with `multiple`, a `string[]`. |

## Don't use

These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.

- `errorMessage`: Not a prop. Ouro UI uses `error`: `<Select error="Required.">`.
- `helperText`: Not a prop. Ouro UI uses `description`: `<Select description="...">`.
- `invalid`: Not a prop. `error` marks the select invalid: `<Select error="What is wrong">`.
- `isDisabled`: Not a prop. Ouro UI uses `disabled`: `<Select disabled>`.
- `isInvalid`: Not a prop. `error` marks the select invalid: `<Select error="What is wrong">`.
- `isMulti`: Not a prop. Ouro UI uses `multiple`: `<Select multiple>`.
- `isRequired`: Not a prop. Ouro UI uses `required`: `<Select required>`.
- `items`: Not a prop. Ouro UI uses `options`: `<Select options={[{ value: "main", label: "Main" }]}>`.
- `onChange`: Not a prop. Select calls `onValueChange` with the value: `<Select onValueChange={(value) => setCourse(value)}>`.

## `className` is for layout only

Pass margins, width and grid placement (`mt-4`, `w-full`, `col-span-2`). Your classes always win over Ouro UI's: Select merges `className` last with `cn` (tailwind-merge), so a class that conflicts with its own replaces it.[^layout-classes]

Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (`hover:`, `sm:`) and any `!` are removed, it starts with `bg-`, `text-`, `border-`, `rounded-`, `shadow-`, `font-`, `ring-`, `from-`, `via-`, `to-`, `fill-` or `stroke-`, or is `border`, `rounded`, `shadow` or `ring`, or has a `dark:` variant. Text wrapping, overflow and table layout classes (`text-wrap`, `text-ellipsis`, `border-collapse`) are layout and do not warn.

Alignment classes (`text-center`) warn too: set alignment on a parent element.

## Parts

For what these props cannot express, pass as children the parts in `@/components/ouro/select/select-parts`. See [parts.md](parts.md#select-parts) for the required nesting.

## Types

### `ErrorMessage`

One error message: text, or a React element for a message that carries a link or other markup.

```ts
type ErrorMessage = string | ReactElement;
```

### `ReadonlyErrorMessages`

A read-only message view accepted by fields and Form; mutable input arrays remain valid.

```ts
type ReadonlyErrorMessages = ErrorMessage | readonly ErrorMessage[];
```

[^layout-classes]: test:registry/ouro/components/ouro/select/select.test.tsx::Select > forwards a ref to the trigger and lets layout classes win verify:testExists(registry/ouro/components/ouro/select/select.test.tsx::Select > forwards a ref to the trigger and lets layout classes win)
