# Select

> A field that opens a bottom sheet list with search, sections and checkmarks that draw themselves in. Single mode closes on a pick; multiple mode shows the picks as chips in the field that grow in and shrink away live behind the sheet.

- Type: Component (inputs)
- Access: Free, open source (MIT)
- Page: https://goodui.dev/native/components/select
- Markdown: https://goodui.dev/native/components/select.md
- Registry item: https://goodui.dev/native/r/select.json
- Dependencies: react-native-reanimated, react-native-svg
- Keywords: react native select, dropdown bottom sheet, picker field expo, multi select chips, searchable select, country picker, combobox mobile

## When to use

- Choosing one or several values from a list too long to show inline: country, category, team members, timezone.
- Lists that need search, sections or a second line of detail per option.

## When not to use

- Two to five short options: use segmented-control or a ChipGroup from chip.
- An ordered range people scroll through (hours, quantity): use wheel-picker.
- Actions rather than values: use action-sheet or context-menu.

## Installation

With the shadcn CLI (needs a components.json in the Expo app), then `npx expo install --fix` so native dependencies match your Expo SDK. The foundation comes along the first time.

```bash
npx shadcn@latest add https://goodui.dev/native/r/select.json
npx expo install --fix
```

The component lands in `components/goodui-native/select/` and imports the foundation from `components/goodui-native/foundation/` with relative paths; it also installs bottom-sheet, button, chip, locale, search-bar.

Built-in strings follow `<GooduiLocaleProvider>` (one [language pack](https://goodui.dev/native/r/locale-tr.json) translates the whole kit); the `labels` prop overrides them for one instance.

## Usage

```tsx
import { useState } from "react";
import { Select } from "@/components/goodui-native/select/select";

export function CountryField() {
  const [country, setCountry] = useState<string[]>([]);
  return (
    <Select
      label="Country"
      placeholder="Choose a country"
      value={country}
      onValueChange={setCountry}
      options={[
        { value: "de", label: "Germany", section: "Europe", keywords: ["DE", "Deutschland"] },
        { value: "fr", label: "France", section: "Europe" },
        { value: "tr", label: "Türkiye", section: "Europe" },
        { value: "us", label: "United States", section: "Americas", keywords: ["USA"] },
        { value: "br", label: "Brazil", section: "Americas" },
      ]}
    />
  );
}
```

## Examples

### Multiple with a limit and chips in the field

```tsx
<Select
  mode="multiple"
  label="Assignees"
  max={3}
  value={people}
  onValueChange={setPeople}
  options={team.map((m) => ({ value: m.id, label: m.name, description: m.role, leading: <Avatar uri={m.photo} size={28} /> }))}
/>
```

### Error and helper text

```tsx
<Select label="Plan" options={plans} value={plan} onValueChange={setPlan} error={plan.length ? undefined : "Choose a plan to continue"} />
```

## API reference

### Select

`import { Select } from "@/components/goodui-native/select/select";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `options` (required) | `SelectOption<T>[]` | – | The choices. Give them a `section` to group the list. |
| `mode` | `"single" \| "multiple"` | `"single"` | `single` closes the sheet on a pick; `multiple` toggles rows and closes with Done. |
| `value` | `T[]` | – | Selected values (controlled). Always an array, also in single mode. |
| `defaultValue` | `T[]` | – | Starting selection when uncontrolled. |
| `onValueChange` | `(value: T[]) => void` | – | Called with the new selection after every pick or removal. |
| `label` | `string` | – | Field label above the value. |
| `placeholder` | `string` | – | Text in the field when nothing is selected. Same as `labels.placeholder`. |
| `title` | `string` | – | Sheet title. Defaults to the label. |
| `description` | `string` | – | Supporting line under the sheet title. |
| `searchable` | `boolean` | – | Show a search field in the sheet. Defaults to on when there are more than 8 options. |
| `searchPlaceholder` | `string` | – | Placeholder of the search field. Same as `labels.searchPlaceholder`. |
| `max` | `number` | – | Multiple mode: the most values that can be selected. Past it a pick is refused with a warning haptic. |
| `maxChips` | `number` | `3` | Multiple mode: how many chips the field shows before "+n". Default 3. |
| `variant` | `"outline" \| "filled"` | `"outline"` | `outline` has a border, `filled` a quiet fill. |
| `error` | `string` | – | Error text under the field; also turns the border red. |
| `helperText` | `string` | – | Help text under the field when there is no error. |
| `disabled` | `boolean` | `false` | Dims the control and ignores presses. |
| `doneLabel` | `string` | – | Label of the button that closes the sheet in multiple mode. Same as `labels.done`. |
| `emptyLabel` | `string` | – | Shown in the sheet when a search matches nothing. Same as `labels.empty`. |
| `open` | `boolean` | – | Controlled sheet state. Usually leave it to the field. |
| `onOpenChange` | `(open: boolean) => void` | – | Called when the sheet opens or closes. |
| `haptic` | `boolean` | `true` | `false` silences the select and warning haptics. |
| `accessibilityLabel` | `string` | – | Screen reader name of the field. Defaults to the label. |
| `labels` | `Partial<SelectLabels>` | – | Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. |
| `style` | `StyleProp<ViewStyle>` | – | Style for the outer container. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

## Accessibility

- The field is a button whose label starts with the visible label, then the value ("Country, Germany"), with expanded and disabled in accessibilityState and aria-expanded and aria-haspopup on the web.
- Rows are radios in a radiogroup (single) or checkboxes (multiple), with checked and disabled states; a description is read after the label.
- Chips in the field have their own "Remove <label>" buttons, so values can be removed without opening the sheet; removal is announced by the chip.
- Hitting max is refused with a warning haptic and announced ("Choose up to 3").
- The sheet is the kit's bottom-sheet: modal, closable with back, the backdrop or a drag, and its grabber is adjustable.
- Rows are at least 52 pt tall; the field at least 48 pt.

## Motion

- The chevron turns 180° on the snappy spring as the sheet opens and back as it closes.
- A picked row's check draws itself in; in single mode the sheet waits 220 ms so the check finishes before it leaves.
- Multiple mode: chips added after the field first rendered grow in from 60 % on the morph spring; removed chips shrink away (chip's own removal).
- The sheet slides with bottom-sheet's springs.
- Reduced motion: the chevron turns with a short timing, checks appear without drawing, chips appear without growing, and single mode closes after 60 ms.

## Haptics

- select when a value is picked or toggled
- warning when max is reached
- chip removal plays the chip's tap
- haptic={false} silences the select and warning

## Performance

- Filtering runs on each keystroke over labels, descriptions and keywords, accent and case insensitive. Rows are plain views in the sheet's scroll view, so keep lists to a few hundred options.

## Notes for AI

- value is always an array, also in single mode: value={[country]} onValueChange={([c]) => setCountry(c)}.
- Search appears by default above 8 options; force it with searchable.
- Group rows with option.section; sections keep the order in which they first appear in options.
- Use keywords for aliases and codes that should match a search but not show.
- Do not wrap Select in your own Pressable or sheet; it owns both.

## Related

- [Bottom sheet](https://goodui.dev/native/components/bottom-sheet.md): A sheet that rests at detents, follows the finger 1:1, settles with velocity and hands drags off to its scroll view.
- [Chip](https://goodui.dev/native/components/chip.md): Filter, choice and input chips: selecting one fills it with the accent while a check draws itself in, removing one shrinks it away, and a ChipGroup in single mode moves one elastic highlight from chip to chip.
- [Search bar](https://goodui.dev/native/components/search-bar.md): An iOS-style search field: focusing it shrinks the field while a Cancel button springs in beside it and scope chips cascade in below. Clear button, loading spinner and debounced queries built in.
- [Wheel picker](https://goodui.dev/native/components/wheel-picker.md): iOS-style wheel: rows sit on a cylinder in 3D perspective, follow the finger 1:1, keep the fling's momentum and settle exactly on a row with a select tick for every row that passes. Compose several into one picker with WheelPickerGroup. On the web it scrolls with the mouse wheel and drags with the mouse.

## Guidance for AI tools

Select: A field that opens a bottom sheet list with search, sections and checkmarks that draw themselves in. Single mode closes on a pick; multiple mode shows the picks as chips in the field that grow in and shrink away live behind the sheet. Use only the documented props. Keep accessibility, reduced motion and both color schemes intact when adapting it.

Full library index: https://goodui.dev/native/llms.txt
