Select

A button that opens a list of choices and shows the one you picked. Use it for forms, filters, pagination, and compact toolbars. The chosen item lines up over the trigger, arrow keys move through the list, and typing jumps to a match.

Guests see the time in their own zone.

[email protected] · 30 minutes · Does not repeat · America/Los_Angeles

Usage

Select usageTSX
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@tulipfarm/sprout/select";

const durations = [
  { value: "30", label: "30 minutes" },
  { value: "60", label: "1 hour" },
  { value: "90", label: "90 minutes" },
];

<label htmlFor="event-duration">Duration</label>
<Select items={durations} value={duration} onValueChange={setDuration}>
  <SelectTrigger id="event-duration" width="full">
    <SelectValue />
  </SelectTrigger>
  <SelectContent>
    {durations.map((item) => (
      <SelectItem key={item.value} value={item.value}>
        {item.label}
      </SelectItem>
    ))}
  </SelectContent>
</Select>

Pass items (an array of { value, label } or a record) so SelectValue can show the label for the stored value. Name the trigger with a label htmlFor or with aria-label.

Sizes and widths

size="default" (32px) is for forms and access rows. size="sm" (28px, small text) is for pagination, filters, and docks. size="calendar" is 32px with small text for calendar headers. width="full"fills the field; width="fit" (the default) sizes to the value. On touch screens every trigger and item is at least 44px tall.

size="default" width="full"Form fields

The team that maintains this agent.

size="default" width="fit"Access rows
Priya Raman[email protected]
size="sm" width="fit"Pagination and toolbars
1–25 of 214
size="sm" width="full"Compact docks
3 proposals need approval
size="calendar" width="fit"Calendar header
June 9 – 15, 2025

Values and placeholders

An empty string is a real value, so “Any type” or “Workspace default” can be chosen. Use placeholder when the value is null. Children on SelectValue replace the label, for example a name looked up from an id. Values can be numbers too, as in the rows-per-page control above.

Empty string is a real value: “Any type”.

Children map the stored id to a name.

Groups and separators

Wrap related items in SelectGroup with a SelectLabel. Use SelectSeparator to set apart an item, like one that is not available on this plan.

Icons and rich items

Items can hold an icon or two lines of text. Long text wraps in the list and is cut off with an ellipsis in the trigger. Disabled items can be reached with arrow keys but can’t be chosen.

Channels already posting here are disabled.

Leave empty to let the workspace choose.

States

Set disabled on Select while a list loads, while work runs, or when it is empty, and add aria-busy to the trigger while loading. Set aria-invalid and point aria-describedby at the error. With required and name, the value is sent with the form, the form won’t submit without it, and a disabled fieldset disables the select.

Locked while the workflow is running.

Loading tables…

No tables yet. Create one in Tables first.

Saving fallback…

Choose a source to continue.

In toolbars and overlays

Small selects sit in filter bars and inline in a sentence. Lists open above popovers and sheets. A trigger can be passed to TooltipTrigger through render.

Select with a tooltipTSX
<Select value={mode} onValueChange={setMode}>
  <Tooltip>
    <TooltipTrigger render={<SelectTrigger size="sm" aria-label="Chat mode" />}>
      <SelectValue>{mode === "plan" ? "Plan" : "Normal"}</SelectValue>
    </TooltipTrigger>
    <TooltipContent>
      Normal acts on your request. Plan only proposes steps first.
    </TooltipContent>
  </Tooltip>
  <SelectContent>
    <SelectItem value="normal">Normal</SelectItem>
    <SelectItem value="plan">Plan only</SelectItem>
  </SelectContent>
</Select>

API

Select is the Base UI select root. It takes value, defaultValue, onValueChange, items, disabled, required, name, and every other root prop.

SelectTrigger takes size ("default","sm", or "calendar") and width ("fit" or "full"), plus id, aria-*, andrender. It passes through field props such as aria-invalid, aria-required, and aria-describedby.

SelectContent renders its own portal, positioner, list, and scroll arrows. It takes side (bottom), align(center), sideOffset (4), alignOffset (0), alignItemWithTrigger (true), collisionPadding (8), and container.

SelectValue, SelectItem, SelectGroup, SelectLabel, SelectSeparator, SelectScrollUpButton, and SelectScrollDownButton wrap the Base UI parts and take their props. Every part sets a data-slot.

Accessibility

The trigger is a combobox with aria-expanded. Enter, Space, and the arrow keys open the list. Arrow keys move, Home and End jump, typing finds a match, Enter picks, and Escape closes and returns focus to the trigger. The chosen item shows a check mark.

The list fades and scales in over 120ms, and only fades when it lines up over the trigger. Motion turns off when reduced motion is requested. In forced-colors mode the highlighted item gets an outline.

Theme tokens

Selects use --sprout-surface, --sprout-input, --sprout-border, --sprout-line, --sprout-hover, --sprout-text, --sprout-muted, --sprout-focus, --sprout-danger, --sprout-shadow-control, --sprout-shadow-overlay, and the control and panel radii. Only the focus ring follows the brand, so they look right with the neutral and TulipFarm themes in light and dark mode.