gray
radius
Forms

Select

One choice from many. A styled listbox rather than a native select, which is a trade rather than a free win.

Basic

Open it and type to jump to an option.

<Select.Root defaultValue="public">
<Select.Trigger>
<Select.Value placeholder="Choose access…" />
</Select.Trigger>
<Select.Content>
<Select.Group>
<Select.Label>Registry</Select.Label>
<Select.Item value="public">Public</Select.Item>
<Select.Item value="restricted">Restricted</Select.Item>
</Select.Group>
<Select.Separator />
<Select.Group>
<Select.Label>Internal</Select.Label>
<Select.Item value="private">Private</Select.Item>
<Select.Item value="none" disabled>Unpublished</Select.Item>
</Select.Group>
</Select.Content>
</Select.Root>
Select.Label only works inside Select.Group and throws if it is not. The label is the group's accessible name, so a label with no group names nothing.
A value too long for the trigger truncates, and the list is free to be wider than the trigger it drops from. The control has a width to keep in a form column; the list only has to be readable, and stops at the edge of the viewport.
A native select gets the platform's own picker on mobile, which usually beats anything a web page can draw. Reach for this one when options need icons, descriptions or grouping the native element cannot express.

In a Field

Field.Control hands its props to the trigger.

Public packages cannot be made private later.

<Field.Root required>
<Field.Label>Access</Field.Label>
<Field.Control>
{props => (
<Select.Root>
<Select.Trigger {...props}>
<Select.Value placeholder="Choose…" />
</Select.Trigger>
<Select.Content>
<Select.Item value="public">Public</Select.Item>
<Select.Item value="private">Private</Select.Item>
</Select.Content>
</Select.Root>
)}
</Field.Control>
<Field.Description>Public packages cannot be made private later.</Field.Description>
</Field.Root>

Parts

Import it as a namespace, and the parts work on either side of the server boundary.

import * as Select from '@minuk-hwang-design-system/components-react/select';
PartWhat it is
Select.RootOwns the value.
Select.TriggerThe control. Sized like Input, with the chevron built in.
Select.ValueRenders the chosen label, or the placeholder.
Select.ContentThe list. Viewport and scroll buttons included.
Select.ItemOne option, with the check-mark gutter.
Select.GroupWraps a label and the items it names. Required around Select.Label.
Select.LabelNames the group it is in. Skipped by the keyboard, and throws outside a Group.
Select.SeparatorDivider between groups.

Props

PropTypeDefaultNotes
valuestringControlled selection, on Root.
defaultValuestringOn Root. The starting choice when the value is left uncontrolled.
onValueChange(value: string) => voidOn Root. Fires with the value of the chosen item.
size's' | 'm' | 'l''m'On Trigger. Height, inset and type, all matching Input.
placeholderstringOn Value. Shown while nothing is chosen.