elements
Demos UI Feed Discuss Install
Style System
  • Style
  • Themes
  • Behaviors
Forms
  • Button
  • ButtonGroup
  • Checkbox
  • ColorPicker
  • Combobox
  • Field
  • FileUpload
  • Input
  • InputGroup
  • InputOTP
  • Label
  • NativeSelect
  • NumberInput
  • PasswordInput
  • RadioGroup
  • Select
  • Slider
  • Switch
  • TagsInput
  • Textarea
  • Toggle
  • ToggleGroup
Overlays
  • AlertDialog
  • Command
  • ContextMenu
  • Dialog
  • Drawer
  • DropdownMenu
  • HoverCard
  • Menubar
  • NavigationMenu
  • Popover
  • Sheet
  • Toast
  • Tooltip
Display
  • Accordion
  • Alert
  • AspectRatio
  • Avatar
  • Badge
  • Breadcrumb
  • Card
  • Collapsible
  • Empty
  • Item
  • Kbd
  • Pagination
  • Progress
  • Separator
  • Skeleton
  • Spinner
  • Table
  • Tabs
  • Tree
Layout
  • Carousel
  • Resizable
  • ScrollArea
  • Sidebar
Dates
  • Calendar
  • DatePicker
  • DateRangePicker
  • TimePicker
Data
  • AreaChart
  • BarChart
  • Chart
  • DataTable
  • Kanban
  • LineChart
  • PieChart
  • RadarChart
  • RadialChart
  • VirtualList
Chat
  • Attachment
  • Message
  • MessageList
  • PromptInput
Elements UI / Forms / Select

Select

Pick one value, or several, from a styled listbox that lines the selected option up over the trigger.

Basic

Click, or focus and press Space, Enter or an arrow. The selected option opens over the trigger; type to jump.

Apple
Banana
Blueberry
Grapes
Pineapple
value: ; complete:
Code
<Select value={form.fruit} placeholder="Select a fruit" label="Fruit">
  <SelectItem value="apple">Apple</SelectItem>
  <SelectItem value="banana">Banana</SelectItem>
  <SelectItem value="blueberry">Blueberry</SelectItem>
  <SelectItem value="grapes">Grapes</SelectItem>
  <SelectItem value="pineapple">Pineapple</SelectItem>
</Select>

Usage

import { Select, SelectItem, SelectGroup, SelectLabel, SelectSeparator } from "@elements/ui/select";

<Select value={form.fruit} placeholder="Select a fruit" name="fruit" label="Fruit">
  <SelectGroup label="Fruits">
    <SelectItem value="apple">Apple</SelectItem>
    <SelectItem value="banana">Banana</SelectItem>
    <SelectItem value="blueberry">Blueberry</SelectItem>
  </SelectGroup>
</Select>

<!-- or from data -->
<Select value={form.theme} items={[
  { value: "light", label: "Light" },
  { value: "dark", label: "Dark" },
  { value: "system", label: "System" },
]} />

Examples

Clearable

An option with an empty value clears the selection, and the trigger shows the placeholder again.

None
Apple
Banana
Grapes
value: banana
Code
<Select value={form.fruit} placeholder="Select a fruit" label="Fruit">
  <SelectItem value="">None</SelectItem>
  <SelectItem value="apple">Apple</SelectItem>
  ...
</Select>

Items

items renders the options from data, and the selected label from the server's first response.

Light
Dark
System
value: system
Code
const THEMES = [
  { value: "light", label: "Light" },
  { value: "dark", label: "Dark" },
  { value: "system", label: "System" },
];

<Select value={prefs.theme} items={THEMES} placeholder="Theme" label="Theme" />

Groups

SelectGroup labels a set of options; SelectSeparator draws a line. Labels, separators and disabled options are skipped.

Fruits
Apple
Banana
Blueberry
Vegetables
Carrot
Leek
Potato
Other
Bread
Code
<Select value={form.food} placeholder="Select a food" label="Food">
  <SelectGroup label="Fruits">
    <SelectItem value="apple">Apple</SelectItem>
    <SelectItem value="banana">Banana</SelectItem>
  </SelectGroup>
  <SelectSeparator />
  <SelectGroup label="Vegetables">
    <SelectItem value="carrot">Carrot</SelectItem>
    <SelectItem value="leek" disabled>Leek</SelectItem>
  </SelectGroup>
</Select>

Scrollable

A long list scrolls inside the room it has. The arrows at the ends scroll while hovered.

North America
Eastern Standard Time (EST)
Central Standard Time (CST)
Mountain Standard Time (MST)
Pacific Standard Time (PST)
Alaska Standard Time (AKST)
Hawaii Standard Time (HST)
Europe and Africa
Greenwich Mean Time (GMT)
Central European Time (CET)
Eastern European Time (EET)
Western European Summer Time (WEST)
Central Africa Time (CAT)
East Africa Time (EAT)
Asia
Moscow Time (MSK)
India Standard Time (IST)
China Standard Time (CST)
Japan Standard Time (JST)
Korea Standard Time (KST)
Indonesia Central Standard Time (WITA)
Australia and Pacific
Australian Western Standard Time (AWST)
Australian Central Standard Time (ACST)
Australian Eastern Standard Time (AEST)
New Zealand Standard Time (NZST)
Fiji Time (FJT)
South America
Argentina Time (ART)
Bolivia Time (BOT)
Brasilia Time (BRT)
Chile Standard Time (CLT)
Code
<Select value={form.zone} placeholder="Select a timezone" label="Timezone">
  <SelectGroup e:for={zone of ZONES} label={zone.group}>
    <SelectItem e:for={[value, label] of zone.items} {value}>{label}</SelectItem>
  </SelectGroup>
</Select>

Position

item-aligned (the default) puts the selected option over the trigger. popper opens beside it, with side, align, offsets and an arrow.

Apple
Banana
Blueberry
Grapes
Pineapple
Apple
Banana
Blueberry
Grapes
Pineapple
Code
<Select position="item-aligned" ...>   <!-- default: the selected item sits over the trigger -->
<Select position="popper" ...>         <!-- below the trigger, like a popover -->
<Select position="popper" side="top" align="end" sideOffset={8} alignOffset={6} arrow ...>

Positioner

arrowPadding keeps the arrow off the popup's corners. collisionAvoidance chooses whether the popup flips or shifts. sticky keeps it in view after the trigger scrolls away. disableAnchorTracking places it when it opens and on a scroll or resize, not when the page shifts under it. backdrop puts an overlay under the popup.

Apple
Banana
Blueberry
Grapes
Pineapple
Apple
Banana
Blueberry
Grapes
Pineapple
Apple
Banana
Blueberry
Grapes
Pineapple
Apple
Banana
Blueberry
Grapes
Pineapple
Apple
Banana
Blueberry
Grapes
Pineapple
Code
<Select position="popper" arrow arrowPadding={12} alignOffset={160} ...>   <!-- the arrow keeps 12px from the corner -->
<Select position="popper" collisionAvoidance={{ side: "none" }} ...>       <!-- never flips; shrinks instead -->
<Select position="popper" sticky ...>                                      <!-- stays in view after the trigger scrolls away -->
<Select position="popper" disableAnchorTracking ...>                       <!-- placed on open, on scroll and on resize only -->
<Select backdrop ...>                                                      <!-- an overlay under the popup -->

Anchor and boundary

With position popper, anchor places the popup against another element than the trigger, and collisionBoundary keeps it inside an element's edges as well as the viewport.

Apple
Banana
Blueberry
Grapes
Pineapple
Anchor
Apple
Banana
Blueberry
Grapes
Pineapple
Code
<span id="fruit-anchor">Anchor</span>
<Select position="popper" anchor="#fruit-anchor" ...>                 <!-- placed against another element -->

<div class="box">
  <Select position="popper" collisionBoundary=".box" ...>             <!-- flips and shrinks to stay in the box -->
</div>

Disabled

A disabled select cannot be focused or opened. A disabled option is shown but skipped.

Apple
Banana
Blueberry
Grapes
Pineapple
Small
Medium (sold out)
Large
Code
<Select disabled value="apple" items={FRUITS} label="Fruit" />
<Select value={form.x} label="Size">
  <SelectItem value="a">Available</SelectItem>
  <SelectItem value="b" disabled>Sold out</SelectItem>
</Select>

Invalid

invalid sets aria-invalid and the danger border. In a Field, the label names the select and the error sits on its reserved line.

Apple
Banana
Blueberry
Grapes
Pineapple
Please select a fruit.
Code
<Field label="Fruit" error={form.fruit ? "" : "Please select a fruit."}>
  <Select value={form.fruit} placeholder="Select a fruit" invalid={!form.fruit} items={FRUITS} />
</Field>

Form

name submits the value with the form; required blocks an empty submit; reset restores the first value.

Apple
Banana
Blueberry
Grapes
Pineapple
Canada
Mexico
United States
Europe
France
Germany
Italy
submitted:
Code
<form onsubmit={(e) => save(e)}>
  <Select name="fruit" required value={form.fruit} items={FRUITS} placeholder="Fruit" label="Fruit" />
  <Select name="country" value={form.country} items={COUNTRIES} autocomplete="country" label="Country" />
  <Button type="submit" variant="primary">Submit</Button>
  <Button type="reset">Reset</Button>
</form>

Controlled

Bind value and open. onValueChange and onOpenChange report only what the select did itself.

Apple
Banana
Blueberry
Grapes
Pineapple
value: apple; open: false; changes: ; opens:
Code
<Select value={state.fruit} open={state.open} label="Fruit"
        onValueChange={(v) => log(v)} onOpenChange={(o) => log(o)}>...</Select>

<Button onclick={() => state.open = true}>Open</Button>
<Button onclick={() => state.fruit = "banana"}>Set banana</Button>

Multiple

multiple toggles options and stays open; values holds them in the order picked, and each submits under name.

Cheese
Mushrooms
Olives
Peppers
Onions
values: cheese; changes: 0
Code
<Select multiple values={form.toppings} placeholder="Toppings" name="toppings" label="Toppings">
  <SelectItem value="cheese">Cheese</SelectItem>
  <SelectItem value="mushrooms">Mushrooms</SelectItem>
  ...
</Select>

Multiple with a count

The display slot shows the first chosen label and how many more are chosen, so a fixed-width trigger never cuts the choice off. position="popper" opens the list below the trigger.

JavaScript
TypeScript
Python
Go
Rust
values: javascript,typescript
Code
<Select multiple values={form.languages} name="languages" position="popper" label="Languages" class="wide">
  <span slot="display">{languagesLabel(form.languages)}</span>
  <SelectItem e:for={l of LANGUAGES} value={l.value}>{l.label}</SelectItem>
</Select>

function languagesLabel(values: string[]): string {
  if (values.length === 0) {
    return "Select languages";
  }

  let first = LANGUAGES.find((l) => l.value === values[0])?.label ?? values[0];

  return values.length === 1 ? first : `${first} (+${values.length - 1} more)`;
}

Sizes

size follows the control height tokens.

Apple
Banana
Blueberry
Grapes
Pineapple
Apple
Banana
Blueberry
Grapes
Pineapple
Apple
Banana
Blueberry
Grapes
Pineapple
Code
<Select size="sm" ...>
<Select ...>
<Select size="lg" ...>

Custom items

Items take icons and any markup; textValue keeps typeahead working, and the indicator slot replaces the check.

Todo
In progress
Done
Code
<Select value={task.status} label="Status">
  <SelectItem value="todo" textValue="Todo">
    <span class="dot is-todo"></span> Todo
  </SelectItem>
  <SelectItem value="done" textValue="Done">
    <Icon name="check-circle" /> Done
    <Icon slot="indicator" name="dot" />
  </SelectItem>
</Select>

Custom trigger content

The display slot renders what the trigger shows, such as the selected person's avatar; label names the trigger. The icon slot replaces the chevron.

AL Ada Lovelace Engineering
GH Grace Hopper Compilers
AT Alan Turing Research
KJ Katherine Johnson Flight
Code
<Select value={task.owner} label="Assignee" class="wide">
  <span slot="display" class="person">
    <span class="avatar">{initials(owner(task.owner)?.name ?? "")}</span>
    {owner(task.owner)?.name ?? "Assign to..."}
  </span>
  <Icon slot="icon" name="chevrons-up-down" />
  <SelectItem e:for={p of PEOPLE} value={p.value} textValue={p.name}>
    <span class="avatar">{initials(p.name)}</span> {p.name}
  </SelectItem>
</Select>

Hide when detached

In a scrolling box, hideWhenDetached hides the popup while its trigger is scrolled out of view, and shows it again when it returns.

Apple
Banana
Blueberry
Grapes
Pineapple
Code
<div class="scroll-box">
  <Select position="popper" modal={false} hideWhenDetached items={FRUITS} label="Fruit" />
</div>

Read only

readOnly keeps the select focusable and submitted, but it does not open or change.

Pacific Standard Time (PST)
Eastern Standard Time (EST)
Code
<Select readOnly value="pst" items={ZONES} label="Timezone" />

Options

loop wraps the arrows; highlightItemOnHover={false} leaves the highlight to the keyboard; onEscapeKeyDown and onPointerDownOutside can keep it open; onCloseAutoFocus can keep focus from returning; open={true} with no binding opens it when it mounts.

Apple
Banana
Blueberry
Grapes
Pineapple
Apple
Banana
Blueberry
Grapes
Pineapple
Apple
Banana
Blueberry
Grapes
Pineapple
Apple
Banana
Blueberry
Grapes
Pineapple
escapes kept: 0; outside kept: 0; focus kept: 0
Code
<Select loop ...>                          <!-- arrows wrap -->
<Select highlightItemOnHover={false} ...>  <!-- the mouse does not move the highlight -->
<Select onEscapeKeyDown={(e) => e.preventDefault()} ...>
<Select onPointerDownOutside={(e) => e.preventDefault()}         <!-- an outside press keeps it open -->
        onCloseAutoFocus={(e) => e.preventDefault()} ...>      <!-- focus is not returned on close -->
<Select open={true} ...>                    <!-- unbound: open when it mounts (defaultOpen) -->

RTL

In a right-to-left region the popup lines up from the right and the check sits on the left.

الفواكه
تفاحة
موز
توت أزرق
Code
<Select dir="rtl" value={form.fruit} placeholder="اختر فاكهة" label="فاكهة">
  <SelectItem value="apple">تفاحة</SelectItem>
  ...
</Select>

Change details

onValueChange and onOpenChange get event details as a second argument: the reason (trigger-press, item-press, escape-key, outside-press), the event and cancel().

Apple
Banana
Blueberry
Grapes
Pineapple
Apple
Banana
Blueberry
Grapes
Pineapple
Apple
Banana
Blueberry
Grapes
Pineapple
Code
<Select value={form.fruit} items={fruits} label="Fruit"
        onValueChange={(value, details) => log(value, details.reason)}
        onOpenChange={(open, details) => log(open, details.reason)} />

<!-- details.cancel() keeps the current state -->
<Select items={fruits} label="Fruit" onValueChange={(value, details) => {
  if (value === "banana") details.cancel();
}} />

Object values

A value can be an object. isItemEqualToValue matches the bound value to its item, itemToStringLabel shows it and itemToStringValue submits it. items also takes a record of value to label, or groups.

Ada Lovelace
Grace Hopper
Alan Turing
Apple
Banana
Cherry
Fruits
Apple
Pear
Vegetables
Leek
Kale
Grace Hopper #2
Code
<Select value={form.user}
        items={USERS.map((user) => ({ value: user }))}
        isItemEqualToValue={(item, value) => item.id === value.id}
        itemToStringLabel={(user) => user.name}
        itemToStringValue={(user) => String(user.id)}
        name="user" label="User" />

<Select items={{ apple: "Apple", banana: "Banana" }} label="Fruit" />            <!-- a record of value to label -->
<Select items={[{ value: "Fruits", items: [...] }, ...]} label="Food" />        <!-- groups -->

Inside a dialog

A select in a modal dialog: Escape and an outside press close the select first and leave the dialog open; a second Escape closes the dialog.

Code
<Dialog title="Edit fruit">
  <Button slot="trigger">Edit fruit</Button>
  <Select value={form.fruit} placeholder="Select a fruit" label="Fruit">
    <SelectItem value="apple">Apple</SelectItem>
    <SelectItem value="banana">Banana</SelectItem>
  </Select>
</Dialog>
Reference Select in the manual Every part, attribute, slot, key and data attribute.
Prev RadioGroupNext Slider
Product
Manual Pricing Compare
Company
History Contact
Legal
Privacy Policy Acceptable Use Terms License Agreement
© Elements Dev Corporation 2026