# Select Field (/components/forms/select-field)

Use `SelectField` when someone chooses one option from a predefined list. It combines a trigger
button, label, description, validation message, popover, and listbox. Use
[`ComboboxField`](/components/forms/combobox-field) instead when searching or filtering the options
helps.

apps/docs/src/examples/select-field/basic.tsx

```tsx
import { SelectField, SelectItem } from '@luke-ui/react/select-field';
import { Stack } from '@luke-ui/react/stack';

const themes = [
	{ id: 'system', label: 'System' },
	{ id: 'light', label: 'Light' },
	{ id: 'dark', label: 'Dark' },
];

export default () => {
	return (
		<Stack gap="space.16">
			<SelectField
				defaultValue="system"
				items={themes}
				label="Theme"
				name="theme"
				placeholder="Choose a theme"
			>
				{(item) => <SelectItem>{item.label}</SelectItem>}
			</SelectField>
		</Stack>
	);
};
```

## Items and selection [#items-and-selection]

Pass `items` and a render function that returns a `SelectItem` for each one. Import `SelectItem`
from `@luke-ui/react/select-field`.

Each option needs a stable id. Give each item in `items` an `id` or `key`, or give each `SelectItem`
an `id` when your data has neither. When the items carry an `id` or `key`, `value`, `onChange`, and
`validate` use the inferred key type, so string ids give `string | null` for `value` and `string`
for `validate`. Otherwise selection values use `Key | null`, and `validate` receives `Key`. React
Aria doesn't call `validate` while nothing is selected.

For a fixed set of options, pass `SelectItem` children with an `id` each instead of `items`.

`SelectField` holds one selected option and has no clear button. An option labelled “None” is a
choice like any other: selecting it selects its key and doesn't empty the field.

Pass `defaultValue` to start with an option selected. Pass `value` and `onChange` to control the
selection yourself. `onChange` receives the selected key, or `null` when nothing is selected, and
`value` takes the same type.

apps/docs/src/examples/select-field/controlled.tsx

```tsx
import { SelectField, SelectItem } from '@luke-ui/react/select-field';
import { Stack } from '@luke-ui/react/stack';
import { Text } from '@luke-ui/react/text';
import { useState } from 'react';

const sizes = [
	{ id: 'small', label: 'Small' },
	{ id: 'default', label: 'Default' },
	{ id: 'large', label: 'Large' },
];

export default () => {
	const [size, setSize] = useState<string | null>('default');

	return (
		<Stack gap="space.16">
			<SelectField items={sizes} label="Text size" onChange={setSize} value={size}>
				{(item) => <SelectItem>{item.label}</SelectItem>}
			</SelectField>
			<Text color="secondary" elementType="p" role="status">
				{`Selected key: ${size ?? 'none'}`}
			</Text>
		</Stack>
	);
};
```

## Required fields [#required-fields]

Set `isRequired` to make a selection mandatory. Use `necessityIndicator` to choose how it appears
beside the label. Read [Required fields](/docs/forms#required-fields) for the indicator values.

A required Select also announces “required” from a hidden cue on the trigger, including when
`necessityIndicator` is `'optional-label'`.

apps/docs/src/examples/select-field/required.tsx

```tsx
import { SelectField, SelectItem } from '@luke-ui/react/select-field';

const countries = [
	{ id: 'australia', label: 'Australia' },
	{ id: 'canada', label: 'Canada' },
	{ id: 'new-zealand', label: 'New Zealand' },
];

export default () => {
	return (
		<SelectField
			description="Choose the country where you work."
			isRequired
			items={countries}
			label="Work location"
			name="workLocation"
			necessityIndicator="required-asterisk"
			placeholder="Choose a country"
		>
			{(item) => <SelectItem>{item.label}</SelectItem>}
		</SelectField>
	);
};
```

## Validation [#validation]

Set `isRequired` or pass `validate` to check the selected key. `SelectField` shows its validation
message after validation fails. Pass `errorMessage` only for an error you already have, such as one
from a form library or your server. A non-empty message marks the field invalid. Read
[Validation](/docs/validation) for where messages come from, server errors, and how to write them.

apps/docs/src/examples/select-field/validation.tsx

```tsx
import { Button } from '@luke-ui/react/button';
import { Cluster } from '@luke-ui/react/cluster';
import { SelectField, SelectItem } from '@luke-ui/react/select-field';
import { Stack } from '@luke-ui/react/stack';
import type { SubmitEvent } from 'react';

const countries = [
	{ id: 'australia', label: 'Australia' },
	{ id: 'canada', label: 'Canada' },
	{ id: 'new-zealand', label: 'New Zealand' },
];

function handleSubmit(event: SubmitEvent<HTMLFormElement>) {
	event.preventDefault();
}

export default () => {
	return (
		<form onSubmit={handleSubmit}>
			<Stack gap="space.16">
				<SelectField
					isRequired
					items={countries}
					label="Country"
					name="country"
					placeholder="Choose a country"
				>
					{(item) => <SelectItem>{item.label}</SelectItem>}
				</SelectField>
				<Cluster>
					<Button type="submit">Submit</Button>
				</Cluster>
			</Stack>
		</form>
	);
};
```

To move focus to the trigger yourself, pass `triggerRef`. `ref` reaches the root element.

## Disabled, pending, and fixed values [#disabled-pending-and-fixed-values]

`SelectField` has no read-only state. Show a value that can't change as text instead.

Set `isDisabled` to block the field. A disabled field can't take focus and doesn't submit its value.

Pass `isPending` while a change is saving. The trigger keeps focus but can't be pressed or opened,
and can't change the value from the trigger, until pending ends.

## Size [#size]

Use `size` to set the control height and typography. Use `small` in compact layouts. `medium` is the
default.

apps/docs/src/examples/select-field/sizes.tsx

```tsx
import { SelectField, SelectItem } from '@luke-ui/react/select-field';
import { Comparison, ComparisonItem } from '#docs';

const options = [
	{ id: 'one', label: 'Example option' },
	{ id: 'two', label: 'Another option' },
];

export default () => {
	return (
		<Comparison>
			<ComparisonItem label="small">
				<SelectField
					items={options}
					label="Example field"
					name="example"
					placeholder="Choose an option"
					size="small"
				>
					{(item) => <SelectItem>{item.label}</SelectItem>}
				</SelectField>
			</ComparisonItem>
			<ComparisonItem label="medium">
				<SelectField
					items={options}
					label="Example field"
					name="example"
					placeholder="Choose an option"
					size="medium"
				>
					{(item) => <SelectItem>{item.label}</SelectItem>}
				</SelectField>
			</ComparisonItem>
		</Comparison>
	);
};
```

## Accessibility [#accessibility]

The select needs an accessible name. Use the visible `label`. Without one, pass `aria-labelledby` to
point at content already on the page, or `aria-label` to supply the name directly. A `placeholder`
doesn't name the field.

## API [#api]

### SelectFieldProps [#selectfieldprops]

### SelectFieldProps

Props for `SelectField`.

`SelectFieldProps` also accepts compatible DOM and ARIA attributes and event handlers for its rendered element.

| Prop                  | Type                                                             | Description                                                                                                                                                               |
| --------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `aria-describedby?`   | `string`                                                         | Identifies the element (or elements) that describes the object.                                                                                                           |
| `aria-details?`       | `string`                                                         | Identifies the element (or elements) that provide a detailed, extended description for the object.                                                                        |
| `onKeyDown?`          | `(e: KeyboardEvent) => void`                                     | Handler that is called when a key is pressed.                                                                                                                             |
| `onKeyUp?`            | `(e: KeyboardEvent) => void`                                     | Handler that is called when a key is released.                                                                                                                            |
| `onFocus?`            | `(e: React.FocusEvent<Element, Element>) => void`                | Handler that is called when the element receives focus.                                                                                                                   |
| `onBlur?`             | `(e: React.FocusEvent<Element, Element>) => void`                | Handler that is called when the element loses focus.                                                                                                                      |
| `onFocusChange?`      | `(isFocused: boolean) => void`                                   | Handler that is called when the element's focus status changes.                                                                                                           |
| `description?`        | `ReactNode`                                                      | Optional helper text shown below the control.                                                                                                                             |
| `necessityIndicator?` | `NecessityIndicator`                                             | Controls how the label identifies required or optional values. Inherits from `Form`, or uses `'required-asterisk'` when unset.                                            |
| `label?`              | `Exclude<ReactNode, boolean \| null \| undefined>`               | Visible label. Pass non-empty, textual, non-interactive content. Place links and buttons outside it, and associate external label content with `aria-labelledby` instead. |
| `aria-label?`         | `string`                                                         |                                                                                                                                                                           |
| `aria-labelledby?`    | `string`                                                         |                                                                                                                                                                           |
| `autoComplete?`       | `string \| undefined`                                            | Describes the type of autocomplete the browser may offer for the hidden form control.                                                                                     |
| `autoFocus?`          | `boolean \| undefined`                                           | Whether the select should receive focus on render.                                                                                                                        |
| `form?`               | `string \| undefined`                                            | The `<form>` element to associate the select with, by id.                                                                                                                 |
| `isDisabled?`         | `boolean \| undefined`                                           | Whether the select is disabled.                                                                                                                                           |
| `isRequired?`         | `boolean \| undefined`                                           | Whether a selection is required before the form can submit.                                                                                                               |
| `name?`               | `string \| undefined`                                            | The name of the select, used when submitting an HTML form.                                                                                                                |
| `validationBehavior?` | `"aria" \| "native" \| undefined`                                | When native HTML form validation runs. Default: `'native'`                                                                                                                |
| `id?`                 | `string`                                                         | Element id for the root element. Use `triggerId` for the trigger button.                                                                                                  |
| `placeholder?`        | `string`                                                         | Text shown in the `SelectValue` while nothing is selected.                                                                                                                |
| `size?`               | `SelectSize`                                                     | Sets the size of the trigger, indicator, and items. Default: `'medium'`                                                                                                   |
| `triggerId?`          | `string \| undefined`                                            | Element id for the trigger button. The root generates one when omitted.                                                                                                   |
| `children`            | `((item: T) => ReactNode) \| ReactNode`                          | The options. Pass a render function that returns a `SelectItem` for each of `items`, or pass static `SelectItem` children.                                                |
| `defaultValue?`       | `NoInfer<ItemKey<T>> \| null`                                    | The initially selected key (uncontrolled). The key type follows the `id` or `key` of the items in `items`.                                                                |
| `errorMessage?`       | `ReactNode`                                                      | Validation message for a controlled error. A non-empty message marks the field invalid.                                                                                   |
| `isPending?`          | `boolean \| undefined`                                           | Whether the field is pending. The trigger keeps focus but can't be pressed or opened, and can't change the value from the trigger, until pending ends.                    |
| `items?`              | `Iterable<T> \| undefined`                                       | Options for the render function in `children`.                                                                                                                            |
| `onChange?`           | `(value: NoInfer<ItemKey<T>> \| null) => void`                   | Called with the new key when the selection changes.                                                                                                                       |
| `ref?`                | `Ref<HTMLDivElement> \| undefined`                               | Forwarded to the field's root element. Use `triggerRef` for the trigger.                                                                                                  |
| `triggerRef?`         | `Ref<HTMLButtonElement>`                                         | Forwarded to the trigger `<button>` element. Accepts a callback ref or a ref object. Use `ref` for the root element.                                                      |
| `validate?`           | `(value: NoInfer<ItemKey<T>>) => ReturnType<SelectRootValidate>` | Custom validation function run against the selected key. Return a message, or `true`/`null` when valid. The key type follows the `id` or `key` of the items in `items`.   |
| `value?`              | `NoInfer<ItemKey<T>> \| null`                                    | The selected key (controlled). Pass `null` for no selection.                                                                                                              |

#### Type definitions

These are the types that this table's signatures and the definitions below name. TypeScript's built-in types, such as `Omit` and `ReadonlyArray`, are not listed.

##### `NecessityIndicator`

```ts
type NecessityIndicator = 'required-asterisk'
  | 'required-label'
  | 'optional-label';
```

##### `SelectSize`

```ts
type SelectSize = keyof typeof selectConfig.variants.size;
```

Which resolves to:

```ts
"medium" | "small"
```

##### `ItemKey`

```ts
type ItemKey<T> = T extends {
  key: infer K extends Key;
} ? K :
T extends {
  id: infer K extends Key;
} ? K : Key;
```

Refers to `Key`.

##### `SelectRootValidate`

```ts
type SelectRootValidate = NonNullable<SelectRootProps['validate']>;
```

Which resolves to:

```ts
(value: Key) => true
  | ValidationError
  | null
  | undefined
```

Refers to `SelectRootProps['validate']`, `Key`, and `ValidationError`.

##### `SelectRootProps['validate']`

Resolves to:

```ts
((value: Key) => true
  | ValidationError
  | null
  | undefined) | undefined
```

Refers to `Key` and `ValidationError`.

##### `KeyboardEvent`

Declared in [`@react-types/shared`](https://www.npmjs.com/package/@react-types/shared) (`src/events.d.ts`).

```ts
type KeyboardEvent = BaseEvent<ReactKeyboardEvent<any>>;
```

Refers to `BaseEvent` and `KeyboardEvent` from `@types/react`.

##### `BaseEvent`

Declared in [`@react-types/shared`](https://www.npmjs.com/package/@react-types/shared) (`src/events.d.ts`).

##### `KeyboardEvent` from `@types/react` (written as `ReactKeyboardEvent`)

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

##### `FocusEvent`

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

##### `ReactNode`

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

```ts
type ReactNode = ReactElement
  | string
  | number
  | bigint
  | Iterable<ReactNode>
  | ReactPortal
  | boolean
  | null
  | undefined
  | DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES[keyof DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES]
  | Promise<AwaitedReactNode>;
```

Refers to `ReactElement`, `ReactPortal`, `DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES`, and `AwaitedReactNode`.

##### `ReactElement`

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

##### `ReactPortal`

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

##### `DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES`

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

##### `AwaitedReactNode`

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

##### `Key`

Declared in [`@react-types/shared`](https://www.npmjs.com/package/@react-types/shared) (`src/key.d.ts`).

```ts
type Key = string | number;
```

##### `Ref`

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

```ts
type Ref<T> = RefCallback<T>
  | RefObject<T | null>
  | null;
```

Refers to `RefCallback` and `RefObject`.

##### `RefCallback`

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

##### `RefObject`

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

##### `ValidationError`

Declared in [`@react-types/shared`](https://www.npmjs.com/package/@react-types/shared) (`src/inputs.d.ts`).

```ts
type ValidationError = string | string[];
```

### SelectItemProps [#selectitemprops]

### SelectItemProps

Props for `SelectItem`.

`SelectItemProps` also accepts compatible DOM and ARIA attributes and event handlers for its rendered element.

| Prop              | Type                                                       | Description                                                                                                                                                                      |
| ----------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `href?`           | `Href`                                                     | A URL to link to. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#href).                                                                                   |
| `hrefLang?`       | `string`                                                   | Hints at the human language of the linked URL. See[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#hreflang).                                                   |
| `target?`         | `HTMLAttributeAnchorTarget`                                | The target window for the link. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target).                                                                   |
| `rel?`            | `string`                                                   | The relationship between the linked resource and the current page. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).                                  |
| `download?`       | `boolean \| string`                                        | Causes the browser to download the linked URL. A string may be provided to suggest a file name. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#download). |
| `ping?`           | `string`                                                   | A space-separated list of URLs to ping when the link is followed. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#ping).                                   |
| `referrerPolicy?` | `HTMLAttributeReferrerPolicy`                              | How much of the referrer to send when following the link. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#referrerpolicy).                                 |
| `onKeyDown?`      | `(e: KeyboardEvent) => void`                               | Handler that is called when a key is pressed.                                                                                                                                    |
| `onKeyUp?`        | `(e: KeyboardEvent) => void`                               | Handler that is called when a key is released.                                                                                                                                   |
| `onFocus?`        | `(e: React.FocusEvent<HTMLDivElement, Element>) => void`   | Handler that is called when the element receives focus.                                                                                                                          |
| `onBlur?`         | `(e: React.FocusEvent<HTMLDivElement, Element>) => void`   | Handler that is called when the element loses focus.                                                                                                                             |
| `onFocusChange?`  | `(isFocused: boolean) => void`                             | Handler that is called when the element's focus status changes.                                                                                                                  |
| `onHoverStart?`   | `(e: HoverEvent) => void`                                  | Handler that is called when a hover interaction starts.                                                                                                                          |
| `onHoverEnd?`     | `(e: HoverEvent) => void`                                  | Handler that is called when a hover interaction ends.                                                                                                                            |
| `onHoverChange?`  | `(isHovering: boolean) => void`                            | Handler that is called when the hover state changes.                                                                                                                             |
| `onPress?`        | `(e: PressEvent) => void`                                  | Handler that is called when the press is released over the target.                                                                                                               |
| `onPressStart?`   | `(e: PressEvent) => void`                                  | Handler that is called when a press interaction starts.                                                                                                                          |
| `onPressEnd?`     | `(e: PressEvent) => void`                                  | Handler that is called when a press interaction ends, either over the target or when the pointer leaves the target.                                                              |
| `onPressChange?`  | `(isPressed: boolean) => void`                             | Handler that is called when the press state changes.                                                                                                                             |
| `onPressUp?`      | `(e: PressEvent) => void`                                  | Handler that is called when a press is released over the target, regardless of whether it started on the target or not.                                                          |
| `children?`       | `ChildrenOrFunction<ListBoxItemRenderProps>`               | The children of the component. A function may be provided to alter the children based on component state.                                                                        |
| `id?`             | `Key`                                                      | The unique id of the item.                                                                                                                                                       |
| `textValue?`      | `string`                                                   | A string representation of the item's contents, used for features like typeahead.                                                                                                |
| `aria-label?`     | `string`                                                   | An accessibility label for this item.                                                                                                                                            |
| `isDisabled?`     | `boolean`                                                  | Whether the item is disabled.                                                                                                                                                    |
| `onAction?`       | `() => void`                                               | Handler that is called when a user performs an action on the item. The exact user event depends on the collection's `selectionBehavior` prop and the interaction modality.       |
| `className?`      | `ClassNameOrFunction<ListBoxItemRenderProps> \| undefined` |                                                                                                                                                                                  |

#### Type definitions

These are the types that this table's signatures and the definitions below name. TypeScript's built-in types, such as `Omit` and `ReadonlyArray`, are not listed.

##### `Href`

Declared in [`@react-types/shared`](https://www.npmjs.com/package/@react-types/shared) (`src/dom.d.ts`).

```ts
type Href = RouterConfig extends {
  href: infer H;
} ? H : string;
```

Refers to `RouterConfig`.

##### `RouterConfig`

Declared in [`@react-types/shared`](https://www.npmjs.com/package/@react-types/shared) (`src/dom.d.ts`).

##### `HTMLAttributeAnchorTarget`

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

```ts
type HTMLAttributeAnchorTarget = "_self"
  | "_blank"
  | "_parent"
  | "_top"
  | (string & {});
```

##### `HTMLAttributeReferrerPolicy`

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

```ts
type HTMLAttributeReferrerPolicy = ""
  | "no-referrer"
  | "no-referrer-when-downgrade"
  | "origin"
  | "origin-when-cross-origin"
  | "same-origin"
  | "strict-origin"
  | "strict-origin-when-cross-origin"
  | "unsafe-url";
```

##### `KeyboardEvent`

Declared in [`@react-types/shared`](https://www.npmjs.com/package/@react-types/shared) (`src/events.d.ts`).

```ts
type KeyboardEvent = BaseEvent<ReactKeyboardEvent<any>>;
```

Refers to `BaseEvent` and `KeyboardEvent` from `@types/react`.

##### `BaseEvent`

Declared in [`@react-types/shared`](https://www.npmjs.com/package/@react-types/shared) (`src/events.d.ts`).

##### `KeyboardEvent` from `@types/react` (written as `ReactKeyboardEvent`)

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

##### `FocusEvent`

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

##### `HoverEvent`

Declared in [`@react-types/shared`](https://www.npmjs.com/package/@react-types/shared) (`src/events.d.ts`).

##### `PressEvent`

Declared in [`@react-types/shared`](https://www.npmjs.com/package/@react-types/shared) (`src/events.d.ts`).

##### `ChildrenOrFunction`

Declared in [`react-aria-components`](https://www.npmjs.com/package/react-aria-components) (`dist/types/src/utils.d.ts`).

```ts
type ChildrenOrFunction<T> = ReactNode | ((values: T & {
  defaultChildren: ReactNode | undefined;
}) => ReactNode);
```

Refers to `ReactNode`.

##### `ReactNode`

Declared in [`@types/react`](https://www.npmjs.com/package/@types/react) (`index.d.ts`).

##### `ListBoxItemRenderProps`

Declared in [`react-aria-components`](https://www.npmjs.com/package/react-aria-components) (`dist/types/src/ListBox.d.ts`).

##### `Key`

Declared in [`@react-types/shared`](https://www.npmjs.com/package/@react-types/shared) (`src/key.d.ts`).

```ts
type Key = string | number;
```

##### `ClassNameOrFunction`

Declared in [`react-aria-components`](https://www.npmjs.com/package/react-aria-components) (`dist/types/src/utils.d.ts`).

```ts
type ClassNameOrFunction<T> = string | ((values: T & {
  defaultClassName: string | undefined;
}) => string);
```
