PettyUI/.firecrawl/kobalte-select2.md
2026-03-31 21:42:28 +07:00

43 KiB

Select

Displays a list of options for the user to pick from — triggered by a button.

Import

Copyts
import { Select } from "@kobalte/core/select";
// or
import { Root, Label, ... } from "@kobalte/core/select";
// or (deprecated)
import { Select } from "@kobalte/core";
Copyts
import { Select } from "@kobalte/core/select";
// or
import { Root, Label, ... } from "@kobalte/core/select";
// or (deprecated)
import { Select } from "@kobalte/core";

Features

  • Exposed to assistive technology as a button with a listbox popup using the WAI ARIA Listbox design pattern.
  • Support for single and multiple selection.
  • Support for disabled options.
  • Labeling support for accessibility.
  • Support for description and error message help text linked to the button via ARIA.
  • Tab stop focus management.
  • Keyboard support for opening the listbox using the arrow keys, including automatically focusing the first or last item accordingly.
  • Typeahead to allow selecting options by typing text, even without opening the listbox.
  • Browser autofill integration via a hidden native <select> element.
  • Supports items, and groups of items.
  • Supports custom placeholder.
  • Can be controlled or uncontrolled.

Anatomy

The select consists of:

  • Select: The root container for a select component.
  • Select.Label: The label that gives the user information on the select.
  • Select.Description: The description that gives the user more information on the select.
  • Select.ErrorMessage: The error message that gives the user information about how to fix a validation error on the select.
  • Select.Trigger: The button that opens the select.
  • Select.Value: The part that reflects the selected value.
  • Select.Icon: A small icon often displayed next to the value as a visual affordance for the fact it can be open.
  • Select.Portal: Portals its children into the body when the select is open.
  • Select.Content: Contains the content to be rendered when the select is open.
  • Select.Arrow: An optional arrow element to render alongside the select content.
  • Select.Listbox: Contains a list of items and allows a user to select one or more of them.
  • Select.Section: Used to render the label of an option group. It won't be focusable using arrow keys.
  • Select.Item: An item of the select.
  • Select.ItemLabel: An accessible label to be announced for the item.
  • Select.ItemDescription: An optional accessible description to be announced for the item.
  • Select.ItemIndicator: The visual indicator rendered when the item is selected.
Copytsx
<Select>
	<Select.Label />
	<Select.Trigger>
		<Select.Value />
		<Select.Icon />
	</Select.Trigger>
	<Select.Description />
	<Select.ErrorMessage />

	<Select.Portal>
		<Select.Content>
			<Select.Arrow />
			<Select.Listbox />
		</Select.Content>
	</Select.Portal>
</Select>
Copytsx
<Select>
	<Select.Label />
	<Select.Trigger>
		<Select.Value />
		<Select.Icon />
	</Select.Trigger>
	<Select.Description />
	<Select.ErrorMessage />

	<Select.Portal>
		<Select.Content>
			<Select.Arrow />
			<Select.Listbox />
		</Select.Content>
	</Select.Portal>
</Select>

Example

Select a fruit…Sort

index.tsxstyle.css

Copytsx
import { Select } from "@kobalte/core/select";
import { CaretSortIcon, CheckIcon } from "some-icon-library";
import "./style.css";

function App() {
  return (
    <Select
      options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
      placeholder="Select a fruit…"
      itemComponent={props => (
        <Select.Item item={props.item} class="select__item">
          <Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
          <Select.ItemIndicator class="select__item-indicator">
            <CheckIcon />
          </Select.ItemIndicator>
        </Select.Item>
      )}
    >
      <Select.Trigger class="select__trigger" aria-label="Fruit">
        <Select.Value class="select__value">
          {state => state.selectedOption()}
        </Select.Value>
        <Select.Icon class="select__icon">
          <CaretSortIcon />
        </Select.Icon>
      </Select.Trigger>
      <Select.Portal>
        <Select.Content class="select__content">
          <Select.Listbox class="select__listbox" />
        </Select.Content>
      </Select.Portal>
    </Select>
  );
}
Copytsx
import { Select } from "@kobalte/core/select";
import { CaretSortIcon, CheckIcon } from "some-icon-library";
import "./style.css";

function App() {
  return (
    <Select
      options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
      placeholder="Select a fruit…"
      itemComponent={props => (
        <Select.Item item={props.item} class="select__item">
          <Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
          <Select.ItemIndicator class="select__item-indicator">
            <CheckIcon />
          </Select.ItemIndicator>
        </Select.Item>
      )}
    >
      <Select.Trigger class="select__trigger" aria-label="Fruit">
        <Select.Value class="select__value">
          {state => state.selectedOption()}
        </Select.Value>
        <Select.Icon class="select__icon">
          <CaretSortIcon />
        </Select.Icon>
      </Select.Trigger>
      <Select.Portal>
        <Select.Content class="select__content">
          <Select.Listbox class="select__listbox" />
        </Select.Content>
      </Select.Portal>
    </Select>
  );
}

Usage

Default value

An initial, uncontrolled value can be provided using the defaultValue prop, which accepts a value corresponding with the options.

BlueberrySort

Copytsx
<Select
	defaultValue="Blueberry"
	options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
	placeholder="Select a fruit…"
	itemComponent={props => (
		<Select.Item item={props.item}>
			<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
			<Select.ItemIndicator>
				<CheckIcon />
			</Select.ItemIndicator>
		</Select.Item>
	)}
>
	<Select.Trigger aria-label="Fruit">
		<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
		<Select.Icon>
			<CaretSortIcon />
		</Select.Icon>
	</Select.Trigger>
	<Select.Portal>
		<Select.Content>
			<Select.Listbox />
		</Select.Content>
	</Select.Portal>
</Select>
Copytsx
<Select
	defaultValue="Blueberry"
	options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
	placeholder="Select a fruit…"
	itemComponent={props => (
		<Select.Item item={props.item}>
			<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
			<Select.ItemIndicator>
				<CheckIcon />
			</Select.ItemIndicator>
		</Select.Item>
	)}
>
	<Select.Trigger aria-label="Fruit">
		<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
		<Select.Icon>
			<CaretSortIcon />
		</Select.Icon>
	</Select.Trigger>
	<Select.Portal>
		<Select.Content>
			<Select.Listbox />
		</Select.Content>
	</Select.Portal>
</Select>

Controlled value

The value prop, which accepts a value corresponding with the options prop, can be used to make the value controlled. The onChange event is fired when the user selects an option, and receives the selected option.

BlueberrySort

Your favorite fruit is: Blueberry.

Copytsx
import { createSignal } from "solid-js";

export function ControlledExample() {
	const [value, setValue] = createSignal("Blueberry");

	return (
		<>
			<Select
				value={value()}
				onChange={setValue}
				options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
				placeholder="Select a fruit…"
				itemComponent={props => (
					<Select.Item item={props.item}>
						<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
						<Select.ItemIndicator>
							<CheckIcon />
						</Select.ItemIndicator>
					</Select.Item>
				)}
			>
				<Select.Trigger aria-label="Fruit">
					<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
					<Select.Icon>
						<CaretSortIcon />
					</Select.Icon>
				</Select.Trigger>
				<Select.Portal>
					<Select.Content>
						<Select.Listbox />
					</Select.Content>
				</Select.Portal>
			</Select>
			<p>Your favorite fruit is: {value()}.</p>
		</>
	);
}
Copytsx
import { createSignal } from "solid-js";

export function ControlledExample() {
	const [value, setValue] = createSignal("Blueberry");

	return (
		<>
			<Select
				value={value()}
				onChange={setValue}
				options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
				placeholder="Select a fruit…"
				itemComponent={props => (
					<Select.Item item={props.item}>
						<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
						<Select.ItemIndicator>
							<CheckIcon />
						</Select.ItemIndicator>
					</Select.Item>
				)}
			>
				<Select.Trigger aria-label="Fruit">
					<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
					<Select.Icon>
						<CaretSortIcon />
					</Select.Icon>
				</Select.Trigger>
				<Select.Portal>
					<Select.Content>
						<Select.Listbox />
					</Select.Content>
				</Select.Portal>
			</Select>
			<p>Your favorite fruit is: {value()}.</p>
		</>
	);
}

Description

The Select.Description component can be used to associate additional help text with a select.

Select a fruit…Sort

Choose the fruit you like the most.

Copytsx
<Select
	options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
	placeholder="Select a fruit…"
	itemComponent={props => (
		<Select.Item item={props.item}>
			<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
			<Select.ItemIndicator>
				<CheckIcon />
			</Select.ItemIndicator>
		</Select.Item>
	)}
>
	<Select.Trigger aria-label="Fruit">
		<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
		<Select.Icon>
			<CaretSortIcon />
		</Select.Icon>
	</Select.Trigger>
	<Select.Description>Choose the fruit you like the most.</Select.Description>
	<Select.Portal>
		<Select.Content>
			<Select.Listbox />
		</Select.Content>
	</Select.Portal>
</Select>
Copytsx
<Select
	options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
	placeholder="Select a fruit…"
	itemComponent={props => (
		<Select.Item item={props.item}>
			<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
			<Select.ItemIndicator>
				<CheckIcon />
			</Select.ItemIndicator>
		</Select.Item>
	)}
>
	<Select.Trigger aria-label="Fruit">
		<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
		<Select.Icon>
			<CaretSortIcon />
		</Select.Icon>
	</Select.Trigger>
	<Select.Description>Choose the fruit you like the most.</Select.Description>
	<Select.Portal>
		<Select.Content>
			<Select.Listbox />
		</Select.Content>
	</Select.Portal>
</Select>

Error message

The Select.ErrorMessage component can be used to help the user fix a validation error. It should be combined with the validationState prop to semantically mark the select as invalid for assistive technologies.

By default, it will render only when the validationState prop is set to invalid, use the forceMount prop to always render the error message (ex: for usage with animation libraries).

GrapesSort

Hmm, I prefer apples.

Copytsx
import { createSignal } from "solid-js";

export function ErrorMessageExample() {
	const [value, setValue] = createSignal("Grapes");

	return (
		<Select
			value={value()}
			onChange={setValue}
			validationState={value() !== "Apple" ? "invalid" : "valid"}
			options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
			placeholder="Select a fruit…"
			itemComponent={props => (
				<Select.Item item={props.item}>
					<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
					<Select.ItemIndicator>
						<CheckIcon />
					</Select.ItemIndicator>
				</Select.Item>
			)}
		>
			<Select.Trigger aria-label="Fruit">
				<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
				<Select.Icon>
					<CaretSortIcon />
				</Select.Icon>
			</Select.Trigger>
			<Select.ErrorMessage>Hmm, I prefer apples.</Select.ErrorMessage>
			<Select.Portal>
				<Select.Content>
					<Select.Listbox />
				</Select.Content>
			</Select.Portal>
		</Select>
	);
}
Copytsx
import { createSignal } from "solid-js";

export function ErrorMessageExample() {
	const [value, setValue] = createSignal("Grapes");

	return (
		<Select
			value={value()}
			onChange={setValue}
			validationState={value() !== "Apple" ? "invalid" : "valid"}
			options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
			placeholder="Select a fruit…"
			itemComponent={props => (
				<Select.Item item={props.item}>
					<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
					<Select.ItemIndicator>
						<CheckIcon />
					</Select.ItemIndicator>
				</Select.Item>
			)}
		>
			<Select.Trigger aria-label="Fruit">
				<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
				<Select.Icon>
					<CaretSortIcon />
				</Select.Icon>
			</Select.Trigger>
			<Select.ErrorMessage>Hmm, I prefer apples.</Select.ErrorMessage>
			<Select.Portal>
				<Select.Content>
					<Select.Listbox />
				</Select.Content>
			</Select.Portal>
		</Select>
	);
}

HTML forms

The select name prop, paired with the Select.HiddenSelect component, can be used for integration with HTML forms.

AppleBananaBlueberryGrapesPineapple

Select a fruit…Sort

ResetSubmit

Copytsx
function HTMLFormExample() {
	const onSubmit = (e: SubmitEvent) => {
		// handle form submission.
	};

	return (
		<form onSubmit={onSubmit}>
			<Select
				name="fruit"
				options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
				placeholder="Select a fruit…"
				itemComponent={props => (
					<Select.Item item={props.item}>
						<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
						<Select.ItemIndicator>
							<CheckIcon />
						</Select.ItemIndicator>
					</Select.Item>
				)}
			>
				<Select.HiddenSelect />
				<Select.Trigger aria-label="Fruit">
					<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
					<Select.Icon>
						<CaretSortIcon />
					</Select.Icon>
				</Select.Trigger>
				<Select.Portal>
					<Select.Content>
						<Select.Listbox />
					</Select.Content>
				</Select.Portal>
			</Select>
			<div>
				<button type="reset">Reset</button>
				<button>Submit</button>
			</div>
		</form>
	);
}
Copytsx
function HTMLFormExample() {
	const onSubmit = (e: SubmitEvent) => {
		// handle form submission.
	};

	return (
		<form onSubmit={onSubmit}>
			<Select
				name="fruit"
				options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
				placeholder="Select a fruit…"
				itemComponent={props => (
					<Select.Item item={props.item}>
						<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
						<Select.ItemIndicator>
							<CheckIcon />
						</Select.ItemIndicator>
					</Select.Item>
				)}
			>
				<Select.HiddenSelect />
				<Select.Trigger aria-label="Fruit">
					<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
					<Select.Icon>
						<CaretSortIcon />
					</Select.Icon>
				</Select.Trigger>
				<Select.Portal>
					<Select.Content>
						<Select.Listbox />
					</Select.Content>
				</Select.Portal>
			</Select>
			<div>
				<button type="reset">Reset</button>
				<button>Submit</button>
			</div>
		</form>
	);
}

Using object as options

Objects can be used as options instead of plain strings. In this case you have to tell the select how it should work with the provided options. For this you have to use the following props :

  • optionValue: The property name to use as the value of an option (submitted with <form>).
  • optionTextValue: The property name to use as the text value of an option for keyboard navigation.
  • optionDisabled: The property name to use as the disabled flag of an option.

Select a food…Sort

Copytsx
interface Fruit {
	value: string;
	label: string;
	disabled: boolean;
}

const options: Fruit[] = [\
	{ value: "apple", label: "Apple", disabled: false },\
	{ value: "banana", label: "Banana", disabled: false },\
	{ value: "blueberry", label: "Blueberry", disabled: false },\
	{ value: "grapes", label: "Grapes", disabled: true },\
	{ value: "pineapple", label: "Pineapple", disabled: false },\
];

function ObjectExample() {
	return (
		<Select
			options={options}
			optionValue="value"
			optionTextValue="label"
			optionDisabled="disabled"
			placeholder="Select a fruit…"
			itemComponent={props => (
				<Select.Item item={props.item}>
					<Select.ItemLabel>{props.item.rawValue.label}</Select.ItemLabel>
					<Select.ItemIndicator>
						<CheckIcon />
					</Select.ItemIndicator>
				</Select.Item>
			)}
		>
			<Select.Trigger aria-label="Fruit">
				<Select.Value<Fruit>>{state => state.selectedOption().label}</Select.Value>
				<Select.Icon>
					<CaretSortIcon />
				</Select.Icon>
			</Select.Trigger>
			<Select.Portal>
				<Select.Content>
					<Select.Listbox />
				</Select.Content>
			</Select.Portal>
		</Select>
	);
}
Copytsx
interface Fruit {
	value: string;
	label: string;
	disabled: boolean;
}

const options: Fruit[] = [\
	{ value: "apple", label: "Apple", disabled: false },\
	{ value: "banana", label: "Banana", disabled: false },\
	{ value: "blueberry", label: "Blueberry", disabled: false },\
	{ value: "grapes", label: "Grapes", disabled: true },\
	{ value: "pineapple", label: "Pineapple", disabled: false },\
];

function ObjectExample() {
	return (
		<Select
			options={options}
			optionValue="value"
			optionTextValue="label"
			optionDisabled="disabled"
			placeholder="Select a fruit…"
			itemComponent={props => (
				<Select.Item item={props.item}>
					<Select.ItemLabel>{props.item.rawValue.label}</Select.ItemLabel>
					<Select.ItemIndicator>
						<CheckIcon />
					</Select.ItemIndicator>
				</Select.Item>
			)}
		>
			<Select.Trigger aria-label="Fruit">
				<Select.Value<Fruit>>{state => state.selectedOption().label}</Select.Value>
				<Select.Icon>
					<CaretSortIcon />
				</Select.Icon>
			</Select.Trigger>
			<Select.Portal>
				<Select.Content>
					<Select.Listbox />
				</Select.Content>
			</Select.Portal>
		</Select>
	);
}

Using option groups

When using option groups you have to tell the select how to distinguish an option from a group. For this you have to use the following props :

  • optionGroupChildren: The property name that refers to the children options of an option group.

Additionally, the sectionComponent prop is used to display the option group label in the select.

Select a food…Sort

Copytsx
interface Food {
	value: string;
	label: string;
	disabled: boolean;
}

interface Category {
	label: string;
	options: Food[];
}

const options: Category[] = [\
	{\
		label: "Fruits",\
		options: [\
			{ value: "apple", label: "Apple", disabled: false },\
			{ value: "banana", label: "Banana", disabled: false },\
			{ value: "blueberry", label: "Blueberry", disabled: false },\
			{ value: "grapes", label: "Grapes", disabled: true },\
			{ value: "pineapple", label: "Pineapple", disabled: false },\
		],\
	},\
	{\
		label: "Meat",\
		options: [\
			{ value: "beef", label: "Beef", disabled: false },\
			{ value: "chicken", label: "Chicken", disabled: false },\
			{ value: "lamb", label: "Lamb", disabled: false },\
			{ value: "pork", label: "Pork", disabled: false },\
		],\
	},\
];

function OptionGroupExample() {
	return (
		<Select<Food, Category>
			options={options}
			optionValue="value"
			optionTextValue="label"
			optionDisabled="disabled"
			optionGroupChildren="options"
			placeholder="Select a food…"
			itemComponent={props => (
				<Select.Item item={props.item}>
					<Select.ItemLabel>{props.item.rawValue.label}</Select.ItemLabel>
					<Select.ItemIndicator>
						<CheckIcon />
					</Select.ItemIndicator>
				</Select.Item>
			)}
			sectionComponent={props => <Select.Section>{props.section.rawValue.label}</Select.Section>}
		>
			<Select.Trigger aria-label="Food">
				<Select.Value<Food>>{state => state.selectedOption().label}</Select.Value>
				<Select.Icon>
					<CaretSortIcon />
				</Select.Icon>
			</Select.Trigger>
			<Select.Portal>
				<Select.Content>
					<Select.Listbox />
				</Select.Content>
			</Select.Portal>
		</Select>
	);
}
Copytsx
interface Food {
	value: string;
	label: string;
	disabled: boolean;
}

interface Category {
	label: string;
	options: Food[];
}

const options: Category[] = [\
	{\
		label: "Fruits",\
		options: [\
			{ value: "apple", label: "Apple", disabled: false },\
			{ value: "banana", label: "Banana", disabled: false },\
			{ value: "blueberry", label: "Blueberry", disabled: false },\
			{ value: "grapes", label: "Grapes", disabled: true },\
			{ value: "pineapple", label: "Pineapple", disabled: false },\
		],\
	},\
	{\
		label: "Meat",\
		options: [\
			{ value: "beef", label: "Beef", disabled: false },\
			{ value: "chicken", label: "Chicken", disabled: false },\
			{ value: "lamb", label: "Lamb", disabled: false },\
			{ value: "pork", label: "Pork", disabled: false },\
		],\
	},\
];

function OptionGroupExample() {
	return (
		<Select<Food, Category>
			options={options}
			optionValue="value"
			optionTextValue="label"
			optionDisabled="disabled"
			optionGroupChildren="options"
			placeholder="Select a food…"
			itemComponent={props => (
				<Select.Item item={props.item}>
					<Select.ItemLabel>{props.item.rawValue.label}</Select.ItemLabel>
					<Select.ItemIndicator>
						<CheckIcon />
					</Select.ItemIndicator>
				</Select.Item>
			)}
			sectionComponent={props => <Select.Section>{props.section.rawValue.label}</Select.Section>}
		>
			<Select.Trigger aria-label="Food">
				<Select.Value<Food>>{state => state.selectedOption().label}</Select.Value>
				<Select.Icon>
					<CaretSortIcon />
				</Select.Icon>
			</Select.Trigger>
			<Select.Portal>
				<Select.Content>
					<Select.Listbox />
				</Select.Content>
			</Select.Portal>
		</Select>
	);
}

Notice the usage of generics on Select for proper TypeScript support.

Multiple selection

The multiple prop can be used to create a select that allow multi-selection. In this case the value provided to value, defaultValue and onChange props is of type Array<T>.

The Select.Value children render prop expose an array of selected options, and two method for removing an option from the selection and clear the selection.

Additionally, the example below uses the as prop to render a div for the Select.Trigger since HTML button can't contain interactive elements according to the W3C.

BlueberryCrossGrapesCross CrossSort

Your favorite fruits are: Blueberry, Grapes.

Copytsx
import { createSignal } from "solid-js";

function MultipleSelectionExample() {
	const [values, setValues] = createSignal(["Blueberry", "Grapes"]);

	return (
		<>
			<Select<string>
				multiple
				value={values()}
				onChange={setValues}
				options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
				placeholder="Select some fruits…"
				itemComponent={props => (
					<Select.Item item={props.item}>
						<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
						<Select.ItemIndicator>
							<CheckIcon />
						</Select.ItemIndicator>
					</Select.Item>
				)}
			>
				<Select.Trigger aria-label="Fruits" as="div">
					<Select.Value<string>>
						{state => (
							<>
								<div>
									<For each={state.selectedOptions()}>
										{option => (
											<span onPointerDown={e => e.stopPropagation()}>
												{option}
												<button onClick={() => state.remove(option)}>
													<CrossIcon />
												</button>
											</span>
										)}
									</For>
								</div>
								<button onPointerDown={e => e.stopPropagation()} onClick={state.clear}>
									<CrossIcon />
								</button>
							</>
						)}
					</Select.Value>
					<Select.Icon>
						<CaretSortIcon />
					</Select.Icon>
				</Select.Trigger>
				<Select.Portal>
					<Select.Content>
						<Select.Listbox />
					</Select.Content>
				</Select.Portal>
			</Select>
			<p>Your favorite fruits are: {values().join(", ")}.</p>
		</>
	);
}
Copytsx
import { createSignal } from "solid-js";

function MultipleSelectionExample() {
	const [values, setValues] = createSignal(["Blueberry", "Grapes"]);

	return (
		<>
			<Select<string>
				multiple
				value={values()}
				onChange={setValues}
				options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
				placeholder="Select some fruits…"
				itemComponent={props => (
					<Select.Item item={props.item}>
						<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
						<Select.ItemIndicator>
							<CheckIcon />
						</Select.ItemIndicator>
					</Select.Item>
				)}
			>
				<Select.Trigger aria-label="Fruits" as="div">
					<Select.Value<string>>
						{state => (
							<>
								<div>
									<For each={state.selectedOptions()}>
										{option => (
											<span onPointerDown={e => e.stopPropagation()}>
												{option}
												<button onClick={() => state.remove(option)}>
													<CrossIcon />
												</button>
											</span>
										)}
									</For>
								</div>
								<button onPointerDown={e => e.stopPropagation()} onClick={state.clear}>
									<CrossIcon />
								</button>
							</>
						)}
					</Select.Value>
					<Select.Icon>
						<CaretSortIcon />
					</Select.Icon>
				</Select.Trigger>
				<Select.Portal>
					<Select.Content>
						<Select.Listbox />
					</Select.Content>
				</Select.Portal>
			</Select>
			<p>Your favorite fruits are: {values().join(", ")}.</p>
		</>
	);
}

Virtual scrolling

When dealing with large collection of items, it's recommended to use a virtual scrolling solution to improve performance.

While Kobalte doesn't provide any built-in virtual scrolling API, it can easily be integrated with a 3rd party library. The example below demonstrate how to virtualize an array of 100,000 options using the @tanstack/solid-virtual package.

Select an item…Sort

Copytsx
import { Select } from "@kobalte/core/select";
import { createVirtualizer } from "@tanstack/solid-virtual";

interface Item {
	value: string;
	label: string;
	disabled: boolean;
}

const options: Item[] = Array.from({ length: 100_000 }, (_, i) => ({
	value: `${i}`,
	label: `Item #${i + 1}`,
	disabled: false,
}));

function SelectContent(props: { options: Item[] }) {
	let listboxRef: HTMLUListElement | undefined;

	const virtualizer = createVirtualizer({
		count: props.options.length,
		getScrollElement: () => listboxRef,
		getItemKey: (index: number) => props.options[index].value,
		estimateSize: () => 32,
		enableSmoothScroll: false,
		overscan: 5,
	});

	return (
		<Select.Content>
			<Select.Listbox
				ref={listboxRef}
				scrollToItem={key =>
					virtualizer.scrollToIndex(props.options.findIndex(option => option.value === key))
				}
				style={{ height: "200px", width: "100%", overflow: "auto" }}
			>
				{items => (
					<div
						style={{
							height: `${virtualizer.getTotalSize()}px`,
							width: "100%",
							position: "relative",
						}}
					>
						<For each={virtualizer.getVirtualItems()}>
							{virtualRow => {
								const item = items().getItem(virtualRow.key);

								if (item) {
									return (
										<Select.Item
											item={item}
											style={{
												position: "absolute",
												top: 0,
												left: 0,
												width: "100%",
												height: `${virtualRow.size}px`,
												transform: `translateY(${virtualRow.start}px)`,
											}}
										>
											<Select.ItemLabel>{item.rawValue.label}</Select.ItemLabel>
											<Select.ItemIndicator>
												<CheckIcon />
											</Select.ItemIndicator>
										</Select.Item>
									);
								}
							}}
						</For>
					</div>
				)}
			</Select.Listbox>
		</Select.Content>
	);
}

function VirtualizedExample() {
	return (
		<Select
			virtualized
			options={options}
			optionValue="value"
			optionTextValue="label"
			optionDisabled="disabled"
			placeholder="Select an item…"
		>
			<Select.Trigger aria-label="Food">
				<Select.Value<Item>>{state => state.selectedOption().label}</Select.Value>
				<Select.Icon>
					<CaretSortIcon />
				</Select.Icon>
			</Select.Trigger>
			<Select.Portal>
				<SelectContent options={options} />
			</Select.Portal>
		</Select>
	);
}
Copytsx
import { Select } from "@kobalte/core/select";
import { createVirtualizer } from "@tanstack/solid-virtual";

interface Item {
	value: string;
	label: string;
	disabled: boolean;
}

const options: Item[] = Array.from({ length: 100_000 }, (_, i) => ({
	value: `${i}`,
	label: `Item #${i + 1}`,
	disabled: false,
}));

function SelectContent(props: { options: Item[] }) {
	let listboxRef: HTMLUListElement | undefined;

	const virtualizer = createVirtualizer({
		count: props.options.length,
		getScrollElement: () => listboxRef,
		getItemKey: (index: number) => props.options[index].value,
		estimateSize: () => 32,
		enableSmoothScroll: false,
		overscan: 5,
	});

	return (
		<Select.Content>
			<Select.Listbox
				ref={listboxRef}
				scrollToItem={key =>
					virtualizer.scrollToIndex(props.options.findIndex(option => option.value === key))
				}
				style={{ height: "200px", width: "100%", overflow: "auto" }}
			>
				{items => (
					<div
						style={{
							height: `${virtualizer.getTotalSize()}px`,
							width: "100%",
							position: "relative",
						}}
					>
						<For each={virtualizer.getVirtualItems()}>
							{virtualRow => {
								const item = items().getItem(virtualRow.key);

								if (item) {
									return (
										<Select.Item
											item={item}
											style={{
												position: "absolute",
												top: 0,
												left: 0,
												width: "100%",
												height: `${virtualRow.size}px`,
												transform: `translateY(${virtualRow.start}px)`,
											}}
										>
											<Select.ItemLabel>{item.rawValue.label}</Select.ItemLabel>
											<Select.ItemIndicator>
												<CheckIcon />
											</Select.ItemIndicator>
										</Select.Item>
									);
								}
							}}
						</For>
					</div>
				)}
			</Select.Listbox>
		</Select.Content>
	);
}

function VirtualizedExample() {
	return (
		<Select
			virtualized
			options={options}
			optionValue="value"
			optionTextValue="label"
			optionDisabled="disabled"
			placeholder="Select an item…"
		>
			<Select.Trigger aria-label="Food">
				<Select.Value<Item>>{state => state.selectedOption().label}</Select.Value>
				<Select.Icon>
					<CaretSortIcon />
				</Select.Icon>
			</Select.Trigger>
			<Select.Portal>
				<SelectContent options={options} />
			</Select.Portal>
		</Select>
	);
}

Origin-aware animations

We expose a CSS custom property --kb-select-content-transform-origin which can be used to animate the content from its computed origin.

Copycss
/* style.css */
.select__content {
	transform-origin: var(--kb-select-content-transform-origin);
	animation: contentHide 250ms ease-in forwards;
}

.select__content[data-expanded] {
	animation: contentShow 250ms ease-out;
}

@keyframes contentShow {
	from {
		opacity: 0;
		transform: translateY(-8px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

@keyframes contentHide {
	from {
		opacity: 1;
		transform: translateY(0);
	}
	to {
		opacity: 0;
		transform: translateY(-8px);
	}
}
Copycss
/* style.css */
.select__content {
	transform-origin: var(--kb-select-content-transform-origin);
	animation: contentHide 250ms ease-in forwards;
}

.select__content[data-expanded] {
	animation: contentShow 250ms ease-out;
}

@keyframes contentShow {
	from {
		opacity: 0;
		transform: translateY(-8px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

@keyframes contentHide {
	from {
		opacity: 1;
		transform: translateY(0);
	}
	to {
		opacity: 0;
		transform: translateY(-8px);
	}
}

API Reference

Select

Select is equivalent to the Root import from @kobalte/core/select (and deprecated Select.Root).

Prop Description
options `Array<T
optionValue `keyof T
optionTextValue `keyof T
optionDisabled `keyof T
optionGroupChildren keyof U
Property name that refers to the children options of an option group.
itemComponent Component<SelectItemComponentProps<T>>
When NOT virtualized, the component to render as an item in the Select.Listbox.
sectionComponent Component<SelectSectionComponentProps<U>>
When NOT virtualized, the component to render as a section in the Select.Listbox.
multiple boolean
Whether the select allows multi-selection.
placeholder JSX.Element
The content that will be rendered when no value or defaultValue is set.
value `T
defaultValue `T
onChange `(value: T
open boolean
The controlled open state of the select.
defaultOpen boolean
The default open state when initially rendered. Useful when you do not need to control the open state.
onOpenChange (open: boolean) => void
Event handler called when the open state of the select changes.
allowDuplicateSelectionEvents boolean
Whether onChange should fire even if the new value is the same as the last.
disallowEmptySelection boolean
Whether the select allows empty selection or not.
closeOnSelection boolean
Whether the select closes after selection.
selectionBehavior `'toggle'
virtualized boolean
Whether the select uses virtual scrolling.
modal boolean
Whether the select should be the only visible content for screen readers, when set to true:
- interaction with outside elements will be disabled.
- scroll will be locked.
- focus will be locked inside the select content.
- elements outside the select content will not be visible for screen readers.
preventScroll boolean
Whether the scroll should be locked even if the select is not modal.
forceMount boolean
Used to force mounting the select (portal, positioner and content) when more control is needed. Useful when controlling animation with SolidJS animation libraries.
name string
The name of the select. Submitted with its owning form as part of a name/value pair.
validationState `'valid'
required boolean
Whether the user must select an item before the owning form can be submitted.
disabled boolean
Whether the select is disabled.
readOnly boolean
Whether the select items can be selected but not changed by the user.
autoComplete string
Describes the type of autocomplete functionality the input should provide if any. See MDN

Select also accepts the following props to customize the placement of the Select.Content.

Prop Description
placement Placement
The placement of the select content.
gutter number
The distance between the select content and the trigger element.
shift number
The skidding of the select content along the trigger element.
flip `boolean
slide boolean
Whether the select content should slide when it overflows.
overlap boolean
Whether the select content can overlap the trigger element when it overflows.
sameWidth boolean
Whether the select content should have the same width as the trigger element. This will be exposed to CSS as --kb-popper-anchor-width.
fitViewport boolean
Whether the select content should fit the viewport. If this is set to true, the select content will have maxWidth and maxHeight set to the viewport size. This will be exposed to CSS as --kb-popper-available-width and --kb-popper-available-height.
hideWhenDetached boolean
Whether to hide the select content when the trigger element becomes occluded.
detachedPadding number
The minimum padding in order to consider the trigger element occluded.
arrowPadding number
The minimum padding between the arrow and the select content corner.
overflowPadding number
The minimum padding between the select content and the viewport edge. This will be exposed to CSS as --kb-popper-overflow-padding.
Data attribute Description
data-valid Present when the select is valid according to the validation rules.
data-invalid Present when the select is invalid according to the validation rules.
data-required Present when the user must select an item before the owning form can be submitted.
data-disabled Present when the select is disabled.
data-readonly Present when the select is read only.

Select.Label, Select.Trigger, Select.Value, Select.Description and Select.ErrorMesssage shares the same data-attributes.

Select.Trigger

Select.Trigger consists of Button.

Data attribute Description
data-expanded Present when the select is open.
data-closed Present when the select is close.

Select.Value

Render Prop Description
selectedOption Accessor<T>
The first (or only, in case of single select) selected option.
selectedOptions Accessor<T[]>
An array of selected options. It will contain only one value in case of single select.
remove (option: T) => void
A function to remove an option from the selection.
clear () => void
A function to clear the selection.
Data attribute Description
data-placeholder-shown Present when the select placeholder is visible (no value selected).

Select.Icon

Data attribute Description
data-expanded Present when the select is open.
data-closed Present when the select is close.

Select.ErrorMessage

Prop Description
forceMount boolean
Used to force mounting when more control is needed. Useful when controlling animation with SolidJS animation libraries.

Select.Content

The popper positioner will copy the same z-index as the Select.Content.

Data attribute Description
data-expanded Present when the select is open.
data-closed Present when the select is close.

Select.Arrow

Prop Description
size number
The size of the arrow.

Select.Listbox

Prop Description
scrollRef `Accessor<HTMLElement
scrollToItem (key: string) => void
When virtualized, the Virtualizer function used to scroll to the item of the given key.
children `(items: Accessor<Collection<CollectionNode<T

Select.Item

Prop Description
item CollectionNode
The collection node to render.
Data attribute Description
data-disabled Present when the item is disabled.
data-selected Present when the item is selected.
data-highlighted Present when the item is highlighted.

Select.ItemLabel, Select.ItemDescription and Select.ItemIndicator shares the same data-attributes.

Select.ItemIndicator

Prop Description
forceMount boolean
Used to force mounting when more control is needed. Useful when controlling animation with SolidJS animation libraries.

Rendered elements

Component Default rendered element
Select div
Select.Label span
Select.Description div
Select.ErrorMessage div
Select.Trigger button
Select.Value span
Select.Icon span
Select.Portal Portal
Select.Content div
Select.Arrow div
Select.Listbox ul
Select.Section li
Select.Item li
Select.ItemLabel div
Select.ItemDescription div
Select.ItemIndicator div

Accessibility

Keyboard Interactions

Key Description
Space When focus is on the trigger, opens the select and focuses the first or selected item.
When focus is on an item, selects the focused item.
Enter When focus is on the trigger, opens the select and focuses the first or selected item.
When focus is on an item, selects the focused item.
ArrowDown When focus is on the trigger, opens the select and focuses the first or selected item.
When focus is on an item, moves focus to the next item.
ArrowUp When focus is on the trigger, opens the select and focuses the last or selected item.
When focus is on an item, moves focus to the previous item.
Home When focus is on an item, moves focus to first item.
End When focus is on an item, moves focus to last item.
ArrowRight In Select, when focus is on the trigger, change the selection to the next item.
ArrowLeft In Select, when focus is on the trigger, change the selection to the previous item.
Shift + ArrowDown In Select, moves focus to and toggles the selected state of the next item.
Shift + ArrowUp In Select, moves focus to and toggles the selected state of the previous item.
Shift + Space In Select, selects contiguous items from the most recently selected item to the focused item.
Ctrl + Shift + Home In Select, selects the focused item and all options up to the first item.
Ctrl + Shift + End In Select, selects the focused item and all options down to the last item.
Ctrl + A In Select, selects all item in the list.
Esc Closes the select and moves focus to the trigger.

Previous←Segmented ControlNextSeparator→