Skip to Content
Select

Select

Select a value from a list of options inside a dropdown menu. This is an alternative to radios when you have a large number of options.

#

Documentation

#

Usage

"use client"; import type { ComponentPropsWithoutRef } from "react"; import { Field, Select, SelectContent, SelectTrigger } from "@optiaxiom/react"; export function App({ description, error, label = "Input label", required = false, }: Pick< ComponentPropsWithoutRef<typeof Field>, "description" | "error" | "label" | "required" >) { return ( <Field description={description} error={error} label={label} required={required} > <Select options={[ { label: "No priority", value: "" }, { label: "Urgent", value: "Urgent" }, { label: "High", value: "High" }, { label: "Medium", value: "Medium" }, { label: "Low", value: "Low" }, ]} > <SelectTrigger placeholder="Set priority" w="224" /> <SelectContent /> </Select> </Field> ); }

#

Anatomy

import { Select, SelectContent, SelectTrigger } from "@optiaxiom/react"; export default () => ( <Select> <SelectTrigger /> <SelectContent /> </Select> );

#

Structure

Select works with lists of items provided via the options prop. The basic structure includes the main component provider, a trigger, and the content.

  • Select
  • SelectTrigger
  • SelectContent

Items must be an array of objects of SelectOption type and at minimum must contain the following two properties:

  • label
  • value

The trigger can be provided a placeholder to display in case no values have been selected yet (or if an empty value is selected).

"use client"; import { Select, SelectContent, SelectTrigger } from "@optiaxiom/react"; export function App() { return ( <Select options={[ { label: "No priority", value: "" }, { label: "Urgent", value: "Urgent" }, { label: "High", value: "High" }, { label: "Medium", value: "Medium" }, { label: "Low", value: "Low" }, ]} > <SelectTrigger placeholder="Set priority" w="224" /> <SelectContent /> </Select> ); }

#

Empty option

An option with value: "" is the “none” choice. Selecting it clears the value, so the trigger shows its placeholder and a required select treats the field as empty, the same as a native <select>. The demo above uses this for “No priority”.

Give the option a non-empty value (e.g. "all") when its label should show in the trigger, such as an “All” filter.

#

Content

Select options use the label property as the display text. We can add the addon and description properties to show additional content inside the items.

"use client"; import { IconCheckCircleSolid, IconCircleSolid, IconCircleXmark, IconSpinner, } from "@optiaxiom/icons"; import { Select, SelectContent, SelectTrigger } from "@optiaxiom/react"; export function App() { return ( <Select options={[ { addon: <IconCircleSolid />, label: "Todo", value: "Todo", }, { addon: <IconSpinner />, label: "In progress", value: "In progress", }, { addon: <IconCheckCircleSolid />, label: "Done", value: "Done", }, { addon: <IconCircleXmark />, label: "Closed", value: "Closed", }, ]} > <SelectTrigger placeholder="Choose status" w="224" /> <SelectContent /> </Select> ); }

#

Controlled

Use the value and defaultValue props to toggle between controlled and uncontrolled usage. And combine it with onValueChange / onChange to listen for changes to the state.

Using onValueChange:

Selected:

"use client"; import { Group, Select, SelectContent, SelectTrigger, Text, } from "@optiaxiom/react"; import { useState } from "react"; const priorities = [ { label: "No priority", value: "" }, { label: "Urgent", value: "Urgent" }, { label: "High", value: "High" }, { label: "Medium", value: "Medium" }, { label: "Low", value: "Low" }, ]; export function App() { const [value, setValue] = useState(""); return ( <Group flexDirection="column" gap="16"> <Select onValueChange={setValue} options={priorities} value={value}> <SelectTrigger placeholder="Set priority" w="224" /> <SelectContent /> </Select> <Text fontSize="md">Selected: {value}</Text> </Group> ); }

Using onChange:

Selected:

"use client"; import { Group, Select, SelectContent, SelectTrigger, Text, } from "@optiaxiom/react"; import { useState } from "react"; const priorities = [ { label: "No priority", value: "" }, { label: "Urgent", value: "Urgent" }, { label: "High", value: "High" }, { label: "Medium", value: "Medium" }, { label: "Low", value: "Low" }, ]; export function App() { const [value, setValue] = useState(""); return ( <Group flexDirection="column" gap="16"> <Select onChange={(event) => setValue(event.target.value)} options={priorities} value={value} > <SelectTrigger placeholder="Set priority" w="224" /> <SelectContent /> </Select> <Text fontSize="md">Selected: {value}</Text> </Group> ); }

#

Form usage

Use the name prop (and optionally ref and onChange) to integrate Select with forms.

Submitted:

"use client"; import { Button, Group, Select, SelectContent, SelectTrigger, Text, } from "@optiaxiom/react"; import { useState } from "react"; const priorities = [ { label: "No priority", value: "" }, { label: "Urgent", value: "Urgent" }, { label: "High", value: "High" }, { label: "Medium", value: "Medium" }, { label: "Low", value: "Low" }, ]; export function App() { const [value, setValue] = useState(""); return ( <form onSubmit={(event) => { event.preventDefault(); if (event.target instanceof HTMLFormElement) { setValue(event.target.priority.value); } }} > <Group alignItems="start" flexDirection="column" gap="16"> <Select defaultValue={value} name="priority" options={priorities}> <SelectTrigger placeholder="Set priority" w="224" /> <SelectContent /> </Select> <Button appearance="primary">Submit</Button> <Text fontSize="md">Submitted: {value}</Text> </Group> </form> ); }

#

Disabled state

Enable the disabled prop on Select to toggle the disabled state of the input field.

"use client"; import type { ComponentPropsWithoutRef } from "react"; import { Select, SelectContent, SelectTrigger } from "@optiaxiom/react"; export function App({ disabled = true, }: Pick<ComponentPropsWithoutRef<typeof Select>, "disabled">) { return ( <Select disabled={disabled} options={[ { label: "No priority", value: "" }, { label: "Urgent", value: "Urgent" }, { label: "High", value: "High" }, { label: "Medium", value: "Medium" }, { label: "Low", value: "Low" }, ]} > <SelectTrigger placeholder="Set priority" w="224" /> <SelectContent /> </Select> ); }

#

Trigger

By default we use the Button component for the menu trigger which accepts all of the existing button props.

We can also use asChild to render a completely different component. We provide a few built-in buttons:

"use client"; import { LabelMenuButton, Select, SelectContent, SelectTrigger, } from "@optiaxiom/react"; export function App() { return ( <Select options={[ { label: "No priority", value: "" }, { label: "Urgent", value: "Urgent" }, { label: "High", value: "High" }, { label: "Medium", value: "Medium" }, { label: "Low", value: "Low" }, ]} > <SelectTrigger asChild w="224"> <LabelMenuButton label="Priority" /> </SelectTrigger> <SelectContent /> </Select> ); }

#

Group

We can group options using the group property.

A group is identified by its label: neighbouring items whose groups share a label render under one heading. Give distinct groups distinct labels, and keep each group’s items next to each other in options.

"use client"; import { Select, SelectContent, SelectTrigger } from "@optiaxiom/react"; const groups = { none: { hidden: true, label: "No priority", }, values: { label: "Priorities", separator: true, }, }; export function App() { return ( <Select options={[ { group: groups.none, label: "No priority", value: "", }, { group: groups.values, label: "Urgent", value: "Urgent", }, { group: groups.values, label: "High", value: "High", }, { group: groups.values, label: "Medium", value: "Medium", }, { group: groups.values, label: "Low", value: "Low", }, ]} > <SelectTrigger placeholder="Set priority" w="224" /> <SelectContent /> </Select> ); }

RadioGroup

Basic control to allow selecting only one item from a set.

Field

Wrapper for inputs to provide context such as label, help text and required state.

Menu

Dropdown menu for displaying actions.

#

Props

#

Select

import { Select } from "@optiaxiom/react"; // or import { Select } from "@optiaxiom/react/select";

Doesn't render its own HTML element.

Prop

children

ReactNode

defaultOpen

The initial open state in uncontrolled mode.

false | true

Default: false

defaultValue

The initial selected value in uncontrolled mode.

string

disabled

Whether the select is disabled.

false | true

loading

Whether to show loading spinner inside the menu.

false | true

name

The name of the inner select element.

string

onBlur

Handler for blur event forwarded to inner select element.

FocusEventHandler<HTMLElement>

onChange

Native change event handler for the hidden select element. Prefer onValueChange for most use cases.

ChangeEventHandler<HTMLSelectElement>

onOpenChange

Handler that is called when the open state changes.

(open: boolean) => void

onValueChange

Handler that is called when the selected value changes. Use this to track the selected value.

(value: string) => void

open

The open state in controlled mode.

false | true

options*

The select items/options we want to render.

readonly SelectOption[] | SelectOption[]

required

Whether the select value is required.

false | true

value

The selected value in controlled mode.

string

#

SelectTrigger

import { SelectTrigger } from "@optiaxiom/react"; // or import { SelectTrigger } from "@optiaxiom/react/select";

Supports all Button props in addition to its own. Renders a <button> element.

Prop

addonAfter

Display content inside the button after children.

ReactNode

addonBefore

Display content inside the button before children.

ReactNode

appearance

Control the appearance by selecting between the different button types.

"default" | "danger" | "primary" | "subtle" | "danger-outline" | "default-opal" | "inverse" | "primary-opal"

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

children

ReactNode

className

string

icon

Display an icon before or after the button content or omit children to only show the icon.

ReactNode

iconPosition

Control whether to show the icon before or after the button content.

"end" | "start"

loading

Show a loading spinner and mark the button aria-busy. The button stays focusable and clickable: don’t also set disabled, which drops keyboard focus mid-action, and ignore repeat clicks in onClick if the action shouldn’t run twice. Nothing is announced when loading starts or ends, so tell screen reader users the outcome yourself, e.g. with a toast.

false | true

placeholder

The placeholder when there is no value.

string

size

Control the size of the button.

"sm" | "md" | "lg"

square

Whether button should have square shape (an icon-only button with no label).

false | true

type

The default behavior of the button.

"button" | "reset" | "submit"

#

SelectContent

import { SelectContent } from "@optiaxiom/react"; // or import { SelectContent } from "@optiaxiom/react/select";

Supports all Box props in addition to its own. Renders a <div> element.

Prop

align

"center" | "end" | "start"

Default: start

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

children

null | string | number | false | true | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | (item: SelectOption) => ReactNode

className

string

maxH

Whether to restrict the max-height of the content.

Content is also restricted by the available height in the screen relative to the trigger.

"xs" | "sm" | "md" | "lg" | "full"

maxW

Whether to restrict the max-width of the content.

Content is also restricted by the available width in the screen.

"xs" | "sm" | "md" | "lg" | "full"

minW

Whether to set the min-width to the width of the trigger.

"0" | "trigger"

side

"bottom" | "left" | "right" | "top"

Default: bottom

#

Accessibility

#

Keyboard interactions

Key

Description

SpaceEnter

When focus is on SelectTrigger, opens the menu and focuses the first item (or selected item). When focus is on an item, activates the focused item and closes the menu.

ArrowDown

When focus is on SelectTrigger, opens the menu and focuses the first item (or selected item). When focus is on an item, moves focus to the next item.

ArrowUp

When focus is on SelectTrigger, opens the menu and focuses the last item (or selected item). When focus is on an item, moves focus to the previous item.

PageUpPageDown

Moves focus up/down by 10 items at a time.

HomeEnd

Moves focus to the first/last item.

Esc

Closes the menu and moves focus back to SelectTrigger.

#

Changelog

#

1.4.0

  • Moved component out of Alpha.

    // Before import { Select, SelectContent, SelectTrigger, } from "@optiaxiom/react/unstable"; // After import { Select, SelectContent, SelectTrigger } from "@optiaxiom/react";

#

1.3.0

  • Renamed items prop to options:

    // Before <Select items={[]} /> // After <Select options={[]} />
  • Removed itemToLabel, itemToValue, and isItemDisabled props in favor of fixed properties on options:

    // Before <Select items={[ { id: "1", name: "Sample", }, ]} itemToLabel={(item) => item.name} itemToValue={(item) => item.id} /> // After <Select options={[ { label: "Sample", value: "1", }, ]} />

#

0.2.0

  • Added component
Last updated on