---
id: "packages/active-ui/native-select"
title: "NativeSelect"
description: "The browser's own select, styled like the other fields, which opens the system picker on a phone."
url: "/packages/active-ui/native-select"
locale: "en"
revision: "4dbb370ccd066657"
---

# NativeSelect

`NativeSelect` is a real `<select>` with the look of the package's fields and a chevron. The
browser draws the list, so on a phone the reader gets the system picker, and a form submits it as
it is. Use it for long lists on mobile, in plain forms, and where a custom popup would be out of
place; use [Select](/packages/active-ui/select) when the options need more than text.

**Example: Grouped options, small and disabled**

Client: Runs entirely in your browser. Source: `examples/ui/native-select.tsx`.

```tsx
// apps/docs/examples/ui/native-select.tsx
'use client';

import {useState} from 'react';

import {
  Field,
  FieldDescription,
  FieldLabel,
} from '@workspace/active-ui/components/primitives/field';
import {
  NativeSelect,
  NativeSelectOptGroup,
  NativeSelectOption,
} from '@workspace/active-ui/components/primitives/native-select';

/**
 * The browser's own select, styled like the other fields: on a phone it
 * opens the system picker. Options can be grouped; the small size suits a
 * toolbar. It is a plain `<select>`, so a form submits it as is.
 */
export default function NativeSelectExample() {
  const [country, setCountry] = useState('NL');
  return (
    <div className="grid w-full max-w-xs gap-6">
      <Field>
        <FieldLabel htmlFor="native-country">Country of residence</FieldLabel>
        <NativeSelect
          id="native-country"
          name="country"
          className="w-full"
          value={country}
          onChange={(event) => setCountry(event.target.value)}
        >
          <NativeSelectOptGroup label="Benelux">
            <NativeSelectOption value="BE">Belgium</NativeSelectOption>
            <NativeSelectOption value="LU">Luxembourg</NativeSelectOption>
            <NativeSelectOption value="NL">Netherlands</NativeSelectOption>
          </NativeSelectOptGroup>
          <NativeSelectOptGroup label="Other">
            <NativeSelectOption value="DE">Germany</NativeSelectOption>
            <NativeSelectOption value="FR">France</NativeSelectOption>
          </NativeSelectOptGroup>
        </NativeSelect>
        <FieldDescription>Value: {country}</FieldDescription>
      </Field>
      <Field>
        <FieldLabel htmlFor="native-rows">Rows per page</FieldLabel>
        <NativeSelect id="native-rows" size="sm" defaultValue="25">
          <NativeSelectOption value="10">10</NativeSelectOption>
          <NativeSelectOption value="25">25</NativeSelectOption>
          <NativeSelectOption value="50">50</NativeSelectOption>
        </NativeSelect>
      </Field>
      <Field data-disabled>
        <FieldLabel htmlFor="native-currency">Currency</FieldLabel>
        <NativeSelect id="native-currency" disabled defaultValue="EUR">
          <NativeSelectOption value="EUR">Euro</NativeSelectOption>
        </NativeSelect>
      </Field>
    </div>
  );
}
```

## Usage

```tsx
import {
  NativeSelect,
  NativeSelectOption,
} from '@workspace/active-ui/components/primitives/native-select';

<NativeSelect
  id="rows"
  value={rows}
  onChange={(event) => setRows(event.target.value)}
>
  <NativeSelectOption value="10">10</NativeSelectOption>
  <NativeSelectOption value="25">25</NativeSelectOption>
</NativeSelect>;
```

## Anatomy

- `NativeSelect`: the `<select>` inside a wrapper that carries the chevron. Every attribute goes to
  the `<select>`; `className` goes to the **wrapper** (so `w-full` widens both), and `size`
  (`default` or `sm`) sets the height. The HTML `size` attribute (visible rows) is not available.
- `NativeSelectOption` and `NativeSelectOptGroup`: `<option>` and `<optgroup>`, in the system
  colours of the open list.

## What your application provides

- A label: a `FieldLabel` with `htmlFor`.
- The options, the value and `onChange`, which receives the native change event.
- A width: the wrapper is as wide as its content (`w-fit`); give it `w-full` in a form.

## Accessibility and keyboard

The browser's own control: `Tab` reaches it, the arrow keys change the value without opening it,
`Alt+↓` or `Space` opens the list, and typing jumps to an option. Disabled, it is skipped.

## API

**NativeSelect**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `size` | `"default" \| "sm"` | no | The height of the field: `default` (36 px) or `sm` (32 px). It replaces the HTML `size` attribute (the number of visible rows). |

**NativeSelectOptGroup**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |

NativeSelectOptGroup has no props of its own.

**NativeSelectOption**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |

NativeSelectOption has no props of its own.

> [!NOTE]
> **Based on shadcn/ui**
>
> This page adapts the Native Select documentation of shadcn/ui (MIT licence) to
> this package; see [Third-party notices](/third-party-notices).
