import { Select, SelectContent, SelectItem, SelectLabel, SelectTrigger } from "@hel/ui/select"
import { Stack } from "@hel/ui/stack"
When to use it#
Use a select when people choose one value from a list that is too long to show all at once: a country, a time zone, a role. For two to five options that people should compare at a glance, a Radio Group is easier to use. If the list is long enough that people need to type to find an item, use a Combobox.
Import#
import {
Select,
SelectTrigger,
SelectContent,
SelectItem,
SelectGroup,
SelectGroupLabel,
SelectLabel,
SelectSeparator,
} from "@hel/ui/select"Anatomy#
Select is a set of small parts, and you put them together. Select holds the value. SelectTrigger is the button that shows it. SelectContent is the popup, and it holds the SelectItem rows.
<Select items={COUNTRIES} defaultValue="iq">
<SelectLabel>Country</SelectLabel>
<SelectTrigger placeholder="Choose a country" />
<SelectContent>
{COUNTRIES.map((country) => (
<SelectItem key={country.value} value={country.value} label={country.label} />
))}
</SelectContent>
</Select>Pass the list to Select as items as well. The trigger uses it to show the label of the chosen item, such as "United Arab Emirates", instead of its value, "ae". It also makes the label show on the first render, before the popup has ever been opened.
SelectLabel names the select for assistive technology, and it is also the visible label. placeholder on the trigger is shown while nothing is chosen.
Examples#
Descriptions#
Each item takes a label and an optional description, which is a good place for the difference between two similar choices. A disabled item stays visible but cannot be chosen.
import { Select, SelectContent, SelectItem, SelectLabel, SelectTrigger } from "@hel/ui/select"
import { Stack } from "@hel/ui/stack"
Groups and separators#
Use SelectGroup with a SelectGroupLabel to put a heading over a part of the list, and SelectSeparator to draw a line between parts.
import {
Select,
SelectContent,In a form#
Give the select a name and the chosen value is submitted with the form. required makes the form wait for a choice.
By default the popup opens over the trigger with the chosen item in place, the way menus on macOS do. Set alignItemWithTrigger={false} on SelectContent to open it below the trigger like an ordinary menu. That suits long lists and triggers that are close to the edge of the screen.
import * as React from "react"
import { Button } from "@hel/ui/button"Long lists#
When the list is taller than the screen allows, SelectContent scrolls inside the same scroll area as the rest of the library.
Motion#
The popup fades in and out, and the trigger gives a small press effect. Set disableAnimation on SelectContent or SelectTrigger to turn that off.
Props#
Select#
| Prop | Type | Default |
|---|---|---|
items | { value, label }[] | none |
value / defaultValue | the item value | none |
onValueChange | (value) => void | none |
multiple | boolean | false |
name | string | none |
disabled / readOnly / required | boolean | false |
It also takes every prop of the Base UI select.
SelectTrigger#
| Prop | Type | Default |
|---|---|---|
placeholder | ReactNode | none |
disableAnimation | boolean | false |
SelectContent#
| Prop | Type | Default |
|---|---|---|
alignItemWithTrigger | boolean | true |
sideOffset | number | 4 |
align | "start" | "center" | "end" | "start" |
disableAnimation | boolean | false |
SelectItem#
| Prop | Type | Default |
|---|---|---|
value | string | required |
label | ReactNode | required |
description | ReactNode | none |
disabled | boolean | false |