On this page

Forms

Select Field

Single-selection field with a label, validation, and a popover list of options.

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 instead when searching or filtering the options helps.

Select Field: Basic
Theme
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

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.

Select Field: Controlled
Text size

Selected key: default

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

Set isRequired to make a selection mandatory. Use necessityIndicator to choose how it appears beside the label. Read 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'.

Select Field: Required
Work locationChoose the country where you work.
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

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 for where messages come from, server errors, and how to write them.

Select Field: Validation
Country
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

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

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

Select Field: Size
small
Example field
medium
Example field
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

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

SelectFieldProps

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

NameTypeDefault
description
ReactNode—

Optional helper text shown below the control.

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.

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.

necessityIndicator
—

Controls how the label identifies required or optional values. Inherits from Form, or uses 'required-asterisk' when unset.

placeholder
string—

Text shown in the SelectValue while nothing is selected.

size
'medium'

Sets the size of the trigger, indicator, and items.

triggerId
string |⁠ undefined—

Element id for the trigger button. The root generates one when omitted.

triggerRef
Ref<HTMLButtonElement>—

Forwarded to the trigger <button> element. Accepts a callback ref or a ref object. Use ref for the root element.

SelectItemProps

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

NameType
download
boolean |⁠ string

Causes the browser to download the linked URL. A string may be provided to suggest a file name. See MDN.

href
Href

A URL to link to. See MDN.

hrefLang
string

Hints at the human language of the linked URL. SeeMDN.

ping
string

A space-separated list of URLs to ping when the link is followed. See MDN.

referrerPolicy
HTMLAttributeReferrerPolicy

How much of the referrer to send when following the link. See MDN.

rel
string

The relationship between the linked resource and the current page. See MDN.

target
HTMLAttributeAnchorTarget

The target window for the link. See MDN.

textValue
string

A string representation of the item's contents, used for features like typeahead.