Skip to Content

Select

A styled single-select dropdown built on Base UI’s Select primitive. Includes trigger, popup, items, grouped options, and built-in Floating UI positioning.

Import

import { Select } from "chunks-ui";

Basic Usage

const fruits = { apple: "Apple", banana: "Banana", cherry: "Cherry", strawberry: "Strawberry", }; <Select.Root items={fruits}> <Select.Trigger aria-label="Fruit"> <Select.Value placeholder="Choose a fruit" /> <Select.Icon /> </Select.Trigger> <Select.Portal> <Select.Positioner sideOffset={5} alignItemWithTrigger={false}> <Select.Popup> <Select.Item value="apple"> <Select.ItemText>Apple</Select.ItemText> <Select.ItemIndicator /> </Select.Item> <Select.Item value="banana"> <Select.ItemText>Banana</Select.ItemText> <Select.ItemIndicator /> </Select.Item> <Select.Item value="cherry"> <Select.ItemText>Cherry</Select.ItemText> <Select.ItemIndicator /> </Select.Item> <Select.Item value="strawberry" disabled> <Select.ItemText>Strawberry</Select.ItemText> <Select.ItemIndicator /> </Select.Item> </Select.Popup> </Select.Positioner> </Select.Portal> </Select.Root>

Grouped Options

const colors = { red: "Red", orange: "Orange", blue: "Blue", green: "Green", }; <Select.Root items={colors}> <Select.Trigger aria-label="Color"> <Select.Value placeholder="Pick a color" /> <Select.Icon /> </Select.Trigger> <Select.Portal> <Select.Positioner sideOffset={5} alignItemWithTrigger={false}> <Select.Popup> <Select.Group> <Select.GroupLabel className="uppercase">Warm</Select.GroupLabel> <Select.Item value="red"> <Select.ItemText>Red</Select.ItemText> <Select.ItemIndicator /> </Select.Item> <Select.Item value="orange"> <Select.ItemText>Orange</Select.ItemText> <Select.ItemIndicator /> </Select.Item> </Select.Group> <Select.Group> <Select.GroupLabel className="uppercase">Cool</Select.GroupLabel> <Select.Item value="blue"> <Select.ItemText>Blue</Select.ItemText> <Select.ItemIndicator /> </Select.Item> <Select.Item value="green"> <Select.ItemText>Green</Select.ItemText> <Select.ItemIndicator /> </Select.Item> </Select.Group> </Select.Popup> </Select.Positioner> </Select.Portal> </Select.Root>

Controlled

const statuses = { active: "Active", inactive: "Inactive", }; function ControlledSelect() { const [value, setValue] = useState<string | null>(null); return ( <Select.Root items={statuses} value={value} onValueChange={setValue}> <Select.Trigger aria-label="Status"> <Select.Value placeholder="Select status" /> <Select.Icon /> </Select.Trigger> <Select.Portal> <Select.Positioner sideOffset={5} alignItemWithTrigger={false}> <Select.Popup> <Select.Item value="active"> <Select.ItemText>Active</Select.ItemText> <Select.ItemIndicator /> </Select.Item> <Select.Item value="inactive"> <Select.ItemText>Inactive</Select.ItemText> <Select.ItemIndicator /> </Select.Item> </Select.Popup> </Select.Positioner> </Select.Portal> </Select.Root> ); }

Disabled

A disabled Select is dimmed and shows the not-allowed cursor. The disabled prop disables it, and so does a disabled Field.Root, Base UI’s Fieldset or a <fieldset disabled>. A disabled Select stays closed whatever open and defaultOpen say, and it ignores a pick. Disabling a Select with an open listbox closes the listbox without calling onOpenChange. When it is enabled again, a controlled listbox opens if open is true; an uncontrolled one stays closed. The disabled prop acts at once; the other causes act when the Select next renders, because it reads the disabled state of its form input after each render, and until then a pick in an open listbox is ignored and closes it. When a disabled Select’s listbox closes with focus in it, focus moves to the page, as it does when any focused control is disabled.

Animation

The popup enters with a scale-up and fade (from scale-95 and opacity-0): the popup spring (springs.popup) with Motion installed, a CSS transition of opacity and scale without it; reduced motion turns off both. Aligned over its trigger (the default), the popup only fades; below the trigger (alignItemWithTrigger={false}), it grows out of the trigger (origin-(--transform-origin)), and a className such as origin-top replaces that origin. On the CSS path, data-[side=none]:data-starting-style:scale-95 and its data-ending-style pair bring the scale back to an aligned popup.

render on Select.Popup replaces the element the spring animates, so a popup with render uses the CSS transition even with Motion installed. To restyle the popup, pass className.

Compound Parts

PartDescription
Select.RootState container. Manages open/close and selected value. items makes the trigger show the selected item’s label: pass an object that maps each value to its label ({ cherry: "Cherry" }), or an array of { value, label }.
Select.TriggerThe button that opens the dropdown. Styled like an input. At most as wide as its container (max-w-full), and it shrinks in a flex row (min-w-0); max-w-none lifts the cap.
Select.ValueDisplays the label of the selected item, or the placeholder text. The label comes from items on Select.Root. Without items, itemToStringLabel or a function as children, a string value shows as is (cherry, not Cherry). A value wider than the trigger stays on one line and ends in an ellipsis before the chevron. It fills the trigger’s free space (flex-1), so an element beside it, such as a badge, keeps its width and the value truncates first; flex-initial in className sizes it by its text instead. In a right-to-left trigger, a long Latin value shows its last words: the line starts at the right edge, so its first words overflow on the left. dir="auto" on the value takes the direction from its first letter: a Latin value then shows its first words and sits at the left, and a right-to-left value stays at the right.
Select.IconChevron icon inside the trigger. Children you pass replace the chevron. With a render element and no children, there is no chevron: the element draws its own, and may be a void element such as <img>.
Select.PortalRenders the popup in a portal.
Select.PositionerHandles Floating UI positioning. Base UI’s default alignItemWithTrigger places the popup over the trigger, with the selected item on top of it; the examples pass alignItemWithTrigger={false} and sideOffset={5} to open it below the trigger instead. Gives the popup the trigger’s width (w-[var(--anchor-width)]). For a trigger sized by its value, pass w-auto min-w-[var(--anchor-width)] in className: the popup is never narrower than the trigger, and its items keep their text on one line. Bare w-auto sizes the popup by its items. Set a width here, not on Select.Popup: a width on the popup spills out of the positioner, and Floating UI’s collision handling measures the positioner.
Select.PopupThe dropdown panel containing items. It is as wide as the trigger, over the trigger or below it. Item text wraps to that width, and a word longer than the popup breaks; the check mark keeps its room at the item’s end.
Select.ItemAn individual selectable option. Put its text in Select.ItemText: only that wraps at the popup’s width, so text placed straight in the item can run past a narrow popup.
Select.ItemTextThe text content of an item. It wraps at the popup’s width and breaks a word longer than that; a badge or shortcut beside it keeps its width.
Select.ItemIndicatorCheckmark shown for the selected item. Children you pass replace the checkmark. With a render element and no children, there is no checkmark: the element draws its own, and may be a void element such as <img>. The indicator is aria-hidden, so screen readers skip its children and announce the option’s selected state instead.
Select.GroupGroups related items together.
Select.GroupLabelLabel for a group of items.

Text Size

The text of Select.Trigger has the size of the text fields: 16px below the md breakpoint (768px by default) and on a touch screen, and 14px otherwise, so a form has one text size. The items follow the field size too, as a popup not opened by touch lays the selected item over the trigger’s value; where the text is 16px, an item is 4px taller (36px). A text-size class on Select.Trigger or Select.Item follows the same rules as on an Input. A size class meant for the whole field goes on both Select.Trigger and Select.Item, or the item opens over the value at a different size. See Text Size in Input.

Props

All parts accept the props from their corresponding Base UI Select sub-components.

Last updated on