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

# NativeSelect

A labeled native `<select>` on the Field contract: pass `label`, `options` (or `<option>` children) and `error`. Phones open the operating system's own picker (a wheel on iOS, a list on Android), which is quick for long plain-text lists such as countries, and works before JavaScript loads.

```tsx
import { NativeSelect } from "@/components/ouro/select/native-select";
```

## Example

```tsx
<NativeSelect
  label="Country"
  name="country"
  autoComplete="country"
  placeholder="Choose a country"
  options={[{ value: "nz", label: "New Zealand" }, { value: "za", label: "South Africa" }]}
  error={errors?.country}
/>
```

## Use instead when

- Use `Select` instead for a styled list (icons, groups with headings, several values with `multiple`), and `RadioGroup` for two to five choices that should all show.

## Props

Type: `NativeSelectProps`.

| Prop | Type | Description |
| --- | --- | --- |
| `children` | `ReactNode` | `<option>` and `<optgroup>` elements, 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. |
| `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`. |
| `name` | `string` | The form field name the value submits under. |
| `onChange` | `SelectHTMLAttributes<HTMLSelectElement>["onChange"]` | Called with the change event when the user picks an option. `onValueChange` gets the value. |
| `options` | `ReadonlyArray<NativeSelectOption \| NativeSelectOptionGroup>` | The choices: `[{ value: "nz", label: "New Zealand" }]`, and groups as `{ label: "Oceania", options: [...] }`. Or pass `<option>` children. |
| `placeholder` | `string` | An empty first option shown, muted, while nothing is chosen: `"Choose a country"`. With `required` it cannot be chosen back, so the browser asks for a real choice. |
| `ref` | `Ref<HTMLSelectElement>` | The select element. |
| `required` | `boolean` | A choice is required: the form will not submit without one, and the label gets a marker. |

## Native attributes

Also accepts the attributes in React's `HTMLAttributes` and `SelectHTMLAttributes` (`id`, `aria-*`, event handlers), passed to the rendered element, except those named on this page.

## 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`: `<NativeSelect error="Required.">`.
- `helperText`: Not a prop. Ouro UI uses `description`: `<NativeSelect description="...">`.
- `invalid`: Not a prop. `error` marks the select invalid: `<NativeSelect error="What is wrong">`.
- `isDisabled`: Not a prop. Ouro UI uses `disabled`: `<NativeSelect disabled>`.
- `isInvalid`: Not a prop. `error` marks the select invalid: `<NativeSelect error="What is wrong">`.
- `isMulti`: Not a prop. For several values use `<Select multiple>`.
- `isRequired`: Not a prop. Ouro UI uses `required`: `<NativeSelect required>`.
- `multiple`: Not supported by NativeSelect. A native multiple select is hard to use; use `<Select multiple>`.

## `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: NativeSelect 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.

## 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;
```

### `NativeSelectOption`

One choice: the `value` it submits and the `label` it shows.

```ts
interface NativeSelectOption {
  /** What the form submits. */
  value: string;
  /** What the picker shows. */
  label: string;
  /** The option shows but cannot be chosen. */
  disabled?: boolean | undefined;
}
```

### `NativeSelectOptionGroup`

A labeled group of options, an `<optgroup>`.

```ts
interface NativeSelectOptionGroup {
  /** The group's heading. */
  label: string;
  /** The options in the group. */
  options: NativeSelectOption[];
}
```

### `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/native-select.test.tsx::NativeSelect > is disabled on request, forwards a ref and lets layout classes win verify:testExists(registry/ouro/components/ouro/select/native-select.test.tsx::NativeSelect > is disabled on request, forwards a ref and lets layout classes win)
