Skip to content

Select

Choose one option from a list (DGA: Dropdown Input). Two sizes, three fills, groups and every state, on Base UI Select.

Version 0.4.1 ·

Select states

Installation

bunx --bun shadcn@latest add @dga/select

Installs Field too. This is DGA's Dropdown Input; it is named Select after the Base UI part.

Usage

page.tsx
tsx
import { Field, FieldLabel } from "@/components/ui/field";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";

const services = [
  { value: "issue", label: "Issue a licence" },
  { value: "renew", label: "Renew a licence" },
];

export function ServiceField() {
  return (
    <Field name="service">
      <FieldLabel required>Service</FieldLabel>
      <Select items={services} required>
        <SelectTrigger>
          <SelectValue placeholder="Choose a service" />
        </SelectTrigger>
        <SelectContent>
          {services.map((s) => (
            <SelectItem key={s.value} value={s.value}>
              {s.label}
            </SelectItem>
          ))}
        </SelectContent>
      </Select>
    </Field>
  );
}

Use Select to pick one option from five to fifteen. For fewer, Radio shows every option at once; for many, add a search field. Pass items so the trigger shows the label of the chosen value, and a placeholder that says what to choose.

Anatomy

  • Trigger (<button>): the border, the chosen label or the placeholder, and a chevron.
  • Content: the popup on the card surface, as wide as the trigger.
  • Item: one option, with a check mark on the end side when chosen.
  • Group, Label and Separator (optional): group related options.

Examples

Basic

Click to open, or focus it and press Enter, Space or ArrowDown.

Basic

States

Default, filled, read-only, disabled and error, and the medium size with a darker fill.

States

Groups

Wrap related options in SelectGroup with a SelectLabel, and separate groups with SelectSeparator.

Groups

RTL

The trigger text starts on the right and the chevron is on the left. The popup opens below the trigger and aligns to its start (right) edge; the check mark sits on the left.

LTR (English)
RTL (Arabic)

Accessibility

KeyAction
TabMoves focus to the trigger.
Enter, Space or ArrowDownOpens the list.
ArrowUp / ArrowDownMoves between options.
Home / EndJumps to the first or last option.
Type a letterJumps to the option that starts with it.
EnterChooses the highlighted option and closes the list.
EscCloses the list and returns focus to the trigger.
  • The trigger has the combobox role with aria-expanded; the list has the listbox role and each option option with aria-selected.
  • The label comes from Field. Helper text and errors are linked with aria-describedby.
  • The highlighted option has a 2px bar and a tint, not a tint alone (WCAG 2.4.7). The chosen option has a check mark.
  • Every state keeps a 3:1 border and 4.5:1 text in light and dark.

API Reference

Select

Lets people pick one option from a list (DGA: Dropdown Input). Compose it from SelectTrigger, SelectValue and SelectContent. Pass `items` so the trigger shows the label of the chosen value, and label it with a Field.

Select props
PropTypeDefault
{ value, label }[] | Record<string, ReactNode>

Data structure of the items rendered in the select popup. When specified, `<Select.Value>` renders the label of the selected item instead of the raw value.

No default
Value

The value of the select. Use when controlled.

No default
Value

The uncontrolled value of the select when it's initially rendered. To render a controlled select, use the `value` prop instead.

No default
boolean

Whether the component should ignore user interaction.

No default
boolean

Whether multiple items can be selected.

No default
string

Identifies the field when a form is submitted.

No default
(value: Value, details) => void

Event handler called when the value of the select changes.

No default
boolean

Whether the user should be unable to choose a different option from the select popup.

No default
boolean

Whether the user must choose a value before submitting a form.

No default

Also accepts every prop of the Base UI Select (opens in a new tab).

SelectContent

The popup list. It opens below the trigger by default and is as wide as the trigger.

SelectContent props
PropTypeDefault
"top" | "bottom" | "inline-start" | "inline-end"

Which side of the anchor element to align the popup against. May automatically change to avoid collisions.

"bottom"
"start" | "center" | "end"

How to align the popup relative to the specified side.

"start"
number

Distance between the anchor and the popup in pixels. Also accepts a function that returns the distance to read the dimensions of the anchor and positioner elements, along with its side and alignment. The function takes a `data` object parameter with the following properties: - `data.anchor`: the dimensions of the anchor element with properties `width` and `height`. - `data.positioner`: the dimensions of the positioner element with properties `width` and `height`. - `data.side`: which side of the anchor element the positioner is aligned against. - `data.align`: how the positioner is aligned relative to the specified side.

4
boolean

Whether the positioner overlaps the trigger so the selected item's text is aligned with the trigger's value text. This only applies to mouse input and is automatically disabled if there is not enough space.

false

Also accepts every prop of the Base UI Select (opens in a new tab).

SelectGroup

Groups related options under a SelectLabel.

Also accepts every prop of the Base UI Select (opens in a new tab).

SelectItem

One option. The chosen option shows a check mark on the end side.

SelectItem props
PropTypeDefault
Value

A unique value that identifies this select item.

No default
boolean

Whether the component should ignore user interaction.

No default

Also accepts every prop of the Base UI Select (opens in a new tab).

SelectLabel

The heading of a SelectGroup.

Also accepts every prop of the Base UI Select (opens in a new tab).

SelectSeparator

Also accepts every prop of the Base UI Select (opens in a new tab).

SelectTrigger

The button that shows the chosen option and opens the list.

SelectTrigger props
PropTypeDefault
"md" | "lg"

Height: `md` 32px, `lg` 40px.

"lg"
"default" | "lighter" | "darker"

Fill: `default` (white), `lighter` or `darker`. All three keep a visible border.

"default"

Also accepts every prop of the Base UI Select (opens in a new tab).

SelectValue

The chosen option, or the `placeholder` while there is none.

SelectValue props
PropTypeDefault
ReactNode

The placeholder value to display when no value is selected. This is overridden by `children` if specified, or by a null item's label in `items`.

No default

Also accepts every prop of the Base UI Select (opens in a new tab).

Deviations from DGA

  • Named Select (Figma: Dropdown Input). Figma draws only the open list; the popup surface is ours.
  • The highlighted option has a bar as well as a tint, because the tint alone is 1.1:1.
  • Same states as Input: no outline, a full underline when focused or open, and the press shows the half underline. A mouse press opens the list straight away, so it goes from rest to open.
ChangeReason
Select: popupThe tint alone is 1.1:1 against the popup, so keyboard users could not see which option is highlighted (WCAG 2.4.7).
Select: nameNamed after the Base UI part and the shadcn convention, so it is found when people search for it. The docs page says it is DGA's Dropdown Input.

View all 2 deviations for this component

Contrast

282 pairs checked · all pass WCAG 2.1 AAShow 51 states
Variant / stateLightDark
filled text18.09:1 AAA15.87:1 AAA
hovered text18.09:1 AAA15.87:1 AAA
focused text10.17:1 AAA12.04:1 AAA
pressed text9.24:1 AAA9.64:1 AAA
pressed placeholder6.69:1 AA5.06:1 AA
pressed prefix and suffix text9.24:1 AAA9.64:1 AAA
underline, focused and pressed18.74:1 3:116.80:1 3:1
placeholder4.77:1 AA6.32:1 AA
prefix and suffix text10.17:1 AAA12.04:1 AAA
icon4.77:1 3:19.38:1 3:1
border, default4.57:1 3:13.80:1 3:1
border, hovered9.73:1 3:112.04:1 3:1
border, focused and pressed17.93:1 3:116.80:1 3:1
underline, error4.62:1 3:14.65:1 3:1
border, error4.62:1 3:14.65:1 3:1
filled text (lighter)17.64:1 AAA14.42:1 AAA
hovered text (lighter)17.64:1 AAA14.42:1 AAA
focused text (lighter)10.17:1 AAA12.04:1 AAA
pressed text (lighter)9.24:1 AAA9.64:1 AAA
pressed placeholder (lighter)6.69:1 AA5.06:1 AA
pressed prefix and suffix text (lighter)9.24:1 AAA9.64:1 AAA
underline, focused and pressed (lighter)18.28:1 3:115.27:1 3:1
placeholder (lighter)4.65:1 AA5.75:1 AA
prefix and suffix text (lighter)9.92:1 AAA10.95:1 AAA
icon (lighter)4.65:1 3:18.52:1 3:1
border, hovered (lighter)9.73:1 3:112.04:1 3:1
border, focused and pressed (lighter)17.93:1 3:116.80:1 3:1
underline, error (lighter)4.62:1 3:14.65:1 3:1
border, error (lighter)4.62:1 3:14.65:1 3:1
filled text (darker)16.44:1 AAA17.04:1 AAA
hovered text (darker)16.44:1 AAA17.04:1 AAA
focused text (darker)10.17:1 AAA12.04:1 AAA
pressed text (darker)9.24:1 AAA9.64:1 AAA
pressed placeholder (darker)6.69:1 AA5.06:1 AA
pressed prefix and suffix text (darker)9.24:1 AAA9.64:1 AAA
underline, focused and pressed (darker)17.03:1 3:118.03:1 3:1
placeholder (darker)6.69:1 AA6.79:1 AA
prefix and suffix text (darker)9.24:1 AAA12.93:1 AAA
icon (darker)4.34:1 3:110.07:1 3:1
border, hovered (darker)9.73:1 3:112.04:1 3:1
border, focused and pressed (darker)17.93:1 3:116.80:1 3:1
underline, error (darker)4.62:1 3:14.65:1 3:1
border, error (darker)4.62:1 3:14.65:1 3:1
border, hover and focus (lighter and darker)4.57:1 3:13.80:1 3:1
read-only text18.09:1 AAA15.87:1 AAA
option text18.09:1 AAA15.87:1 AAA
option text, highlighted16.44:1 AAA12.71:1 AAA
group label4.77:1 AA6.32:1 AA
selected check4.74:1 3:110.38:1 3:1
selected check, highlighted4.31:1 3:18.31:1 3:1
highlight bar18.09:1 3:117.49:1 3:1

Text needs 4.5:1, icons and focus rings 3:1. Disabled states are exempt. Full contrast report