Skip to content

Select

Picks one value from a short list in a dropdown, as a picklist does.

Library
What’s this?

Installation

pnpm dlx shadcn@latest add @wisty/select

The CLI installs the Radix or Base UI file to match your project.

If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.

When to use

Use it

  • To pick one value from a short, fixed list, like an opportunity's stage or an account's type: the picklist of record forms.
  • For roughly 4 to 15 options, known up front, that don't need searching.

Don't use it

  • Two or three options with room to show them. Use Radio group, or Checkbox for yes or no.
  • People should type to search a long list, like countries. Use Combobox. To pick a record, like an account, use Lookup.
  • People pick several values. Use Checkbox.
  • The list holds actions, like Edit or Delete. Use Dropdown menu.
  • A plain form post, or the phone's own picker on mobile. Use Native select.

Usage

Select
├── SelectTrigger
│   └── SelectValue
└── SelectContent
    ├── SelectGroup
    │   ├── SelectLabel
    │   └── SelectItem
    ├── SelectSeparator
    └── SelectItem

To control the value, pass value and onValueChange:

import { useState } from "react"

import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"

export function PrioritySelect() {
  const [priority, setPriority] = useState("Medium")

  return (
    <Select
      value={priority}
      onValueChange={(next) => setPriority(next ?? "Medium")}
    >
      <SelectTrigger aria-label="Priority">
        <SelectValue />
      </SelectTrigger>
      <SelectContent>
        <SelectItem value="High">High</SelectItem>
        <SelectItem value="Medium">Medium</SelectItem>
        <SelectItem value="Low">Low</SelectItem>
      </SelectContent>
    </Select>
  )
}

The bases differ in three places:

  • In Radix projects, onValueChange gets the value. In Base UI projects its type allows null and it gets event details as a second argument, so the snippet falls back on null.
  • In Radix projects, the trigger shows the selected option's text. In Base UI projects it shows the value unless you pass items, a list of { value, label }, to Select. Pass items when a value doesn't read as its label, like needs-analysis.
  • To open the list over the field, with the selected option on it, pass position="item-aligned" to SelectContent in Radix projects, or alignItemWithTrigger in Base UI projects.

Examples

Groups

Options in `SelectGroup`s under `SelectLabel`s, a `SelectSeparator` between them, a `disabled` option and a selected value.

Sizes

The default 32px trigger and the 28px one, set with `size` on `SelectTrigger`, each named by `aria-label` as in a toolbar.

States

A picklist with `aria-invalid` on `SelectTrigger` and an error, and one with `disabled` on `Select`.

Long list

Account's 32 industries: the list shows seven options and scrolls through the rest, as SLDS's picklist does.

API reference

SLDS mapping

Select is SLDS's picklist, the read-only combobox (slds-combobox with slds-input_faux), not the native slds-select.

  • SelectTrigger is slds-input_faux slds-combobox__input with its chevron (slds-input__icon_right). It takes the input's border, focus, error and disabled styles.
  • SelectValue is the field's slds-truncate text.
  • SelectContent is slds-dropdown slds-dropdown_fluid slds-dropdown_length-with-icon-7.
  • SelectItem is slds-listbox__option slds-listbox__option_plain, with the check in slds-listbox__option-icon on the left.
  • SelectLabel is slds-listbox__option-header, and SelectSeparator is the slds-has-divider_top-space line.

Known gaps:

  • No option icons, no option metadata lines, and no five- or ten-row list (slds-dropdown_length-5, -10).
PropValueSLDSNotes
SelectTrigger sizedefaultslds-input_faux slds-combobox__input32px, like an input.
SelectTrigger sizesmnoneSLDS has no small picklist. 28px, like a small button.

Keyboard interactions

Keys Action
Enter, Space or Down Arrow On the trigger, opens the list with the selected option highlighted.
Down Arrow, Up Arrow Moves the highlight to the next or previous option.
Home, End Moves the highlight to the first or last option.
Letters Highlights the next option whose label starts with them. On the closed trigger, selects it.
Enter Selects the highlighted option, closes the list and returns focus to the trigger.
Escape Closes the list without changing the value and returns focus to the trigger.

Accessibility

  • Name the trigger: give it an id and point a FieldLabel's htmlFor at it, or pass aria-label.
  • For an error, set aria-invalid on SelectTrigger and point aria-describedby at the message.
  • In Cosmos dark, the error text in an invalid field is about 4.3:1, below WCAG AA's 4.5:1. SLDS's own invalid combobox is the same.
  • The highlighted option shows an inset ring on keyboard focus, so the highlight isn't color alone. Keep it if you restyle items.

Motion

  • The list appears and disappears at once, with no fade or zoom. If you add animation classes through className, put them behind motion-safe:.

Responsive behavior

  • The trigger is as wide as its content. Pass className="w-full" to fill a form column.
  • The list is at least as wide as the trigger. It shows seven rows, then scrolls, and opens above the field when there's no room below.
  • A long label is cut off on one line in the trigger. Keep option labels short.

Rules

  • Give every SelectItem a unique value.
  • Put SelectValue inside SelectTrigger, with a placeholder for an empty select.
  • Don't restyle the trigger's border, focus ring or error colors. They come from the input tokens and change per theme.
  • Don't put buttons, links or other interactive elements inside SelectItem.