Select

A dropdown for picking one value from a list. Built on Radix Select for keyboard navigation, type-ahead and a11y, with interruptible enter and exit. Its sibling SelectSearch adds a search field.

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Add this component (its dependencies come along)npx koalaui-cli@latest add select

Usage#

usage.tsxTSX
import { Pizza, Hamburger } from "@phosphor-icons/react"import {  Select, SelectTrigger, SelectValue, SelectContent,  SelectItem, SelectGroup, SelectLabel, SelectSeparator,} from "@/components/ui/select"export function Example() {  return (    <Select>      <SelectTrigger className="w-56">        <SelectValue placeholder="Select a dish" />      </SelectTrigger>      <SelectContent>        <SelectItem value="pizza">          <span className="flex items-center gap-2"><Pizza /> Pizza</span>        </SelectItem>        <SelectItem value="burger">          <span className="flex items-center gap-2"><Hamburger /> Hamburger</span>        </SelectItem>      </SelectContent>    </Select>  )}

Groups and labels#

Use SelectGroup and SelectLabel to group related options. Add SelectSeparator between groups for visual clarity.

Long lists#

When the options outgrow the available height the menu scrolls, and its top and bottom edges fade softly into the popover, the scroll fade utility, so there’s a clear “more above / below” cue without a scrollbar. Open this and scroll: the leading edge stays crisp, the trailing edge dissolves, and a keyboard-focused row always lands clear of the fade. The list is capped at 20rem or the room left on screen, whichever is smaller, so on a phone it scrolls inside the panel instead of running off the viewport, and the arrows at either end sit over the fade rather than pushing the rows down.

Searching the list#

Past a couple of dozen options, scrolling stops being a way to find anything. SelectSearch is the same select with a search field in its panel: one value, the same trigger, the same rows and the same brand check, filtered as you type. Focus stays in the field the whole time, so typing never stops: arrows move the highlight, Enter commits the active row, Escape closes. Give each row a textValue (what the search reads, since value is an id) and keywords for the terms people actually type - here, a city finds its timezone. Rows that don’t match render nothing, so groups, separators and the empty state collapse on their own.

Pass inline and the panel renders in the page flow under its trigger instead of floating over it, for a picker that belongs to a form or a side panel. In flow it paints no fill and no shadow, just the hairline, because fill is elevation; the trigger stays quiet too, since the search field inside the panel is what holds the focus. This one also shows renderValue: the value is seeded from outside and the rows only exist while the panel is open, so the trigger is told how to draw it.

AAva Smith · Design lead
IIsaac Reed · Engineering
NNora Lewis · Product
JJames Brennan · Engineering
GGabriel Torres · Support

Aligned to the selected item#

By default the list opens below the trigger, which keeps the trigger’s brand ring in view and matches the pickers built on Popover (CountrySelect, MultiSelect, DatePicker). Pass position="item-aligned" for a short, toolbar-style list: it opens with the current option laid over the trigger, so the eye never moves. Keep it to a handful of options; a long list belongs below the trigger.

Country select#

A plain Select over ~250 countries is a long scroll, so reach for CountrySelect, a searchable picker that looks like a Select trigger but filters as you type and is fully keyboard-navigable. It returns the selected ISO 3166-1 alpha-2 code and lives in the Input family, sharing the canonical country dataset and circular flags with PhoneInput. Inside a Field it wires up label, aria, and error state automatically.

Disabled items#

Option hints#

Some labels are too terse to stand on their own. High and Medium say nothing about the trade-off behind them. Pass a tooltip to SelectItem to attach a hint that surfaces on hover and on keyboard focus, so the choice is informed without crowding the row. Use tooltipPlacement to change the side it grows toward (defaults to right).

Density#

The trigger height comes from size on SelectTrigger - sm/md/lg map to 32/36/40px, the same scale as Button and Input, so a Select lines up with them at a shared size. density no longer sets the trigger height; it picks the default size when you do not pass one (compact → sm, comfortable → md) and tightens the dropdown menu rows. Pass density to SelectContent (or a parent DensityProvider) for the menu spacing.

Selecting multiple values#

Select picks exactly one value and closes on pick. For checkboxes, switches, and sections that stay open while you toggle several values, reach for Multi Select - a Popover-backed sibling with the same surface and motion.

FAQ#

Use `Select` to pick exactly one value from a short list; it closes on pick. Past a couple of dozen options, `SelectSearch` is the same select with a search field in its panel. For toggling several values with checkboxes or switches that stay open, reach for `MultiSelect`, and for the ~250-country case use the ready-made `CountrySelect` from the Input family.