Displays a list of options for the user to pick from—triggered by a button.
<Select.root id="select-primitive">
<Select.trigger
class="DemoSelectTrigger"
aria-label="Food"
>
<Select.value placeholder="Select a fruit…" />
<Select.icon class="DemoSelectIcon">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.icon>
</Select.trigger>
<Select.content
class="DemoSelectContent"
id="select-primitive-content"
>
<Select.scroll_up_button class="DemoSelectScrollButton">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M3.13523 8.84197C3.3241 9.04343 3.64052 9.05363 3.84197 8.86477L7.5 5.43536L11.158 8.86477C11.3595 9.05363 11.6759 9.04343 11.8648 8.84197C12.0536 8.64051 12.0434 8.32409 11.842 8.13523L7.84197 4.38523C7.64964 4.20492 7.35036 4.20492 7.15803 4.38523L3.15803 8.13523C2.95657 8.32409 2.94637 8.64051 3.13523 8.84197Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.scroll_up_button>
<Select.viewport class="DemoSelectViewport">
<Select.group>
<Select.label class="DemoSelectLabel">Fruits</Select.label>
<Select.item
class="DemoSelectItem"
value="apple"
>
<Select.item_text>Apple</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
<Select.item
class="DemoSelectItem"
value="banana"
>
<Select.item_text>Banana</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
<Select.item
class="DemoSelectItem"
value="blueberry"
>
<Select.item_text>Blueberry</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
<Select.item
class="DemoSelectItem"
value="grapes"
>
<Select.item_text>Grapes</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
<Select.item
class="DemoSelectItem"
value="pineapple"
>
<Select.item_text>Pineapple</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
<Select.item
class="DemoSelectItem"
value="orange"
>
<Select.item_text>Orange</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
</Select.group>
<Select.separator class="DemoSelectSeparator" />
<Select.group>
<Select.label class="DemoSelectLabel">Vegetables</Select.label>
<Select.item
class="DemoSelectItem"
value="aubergine"
>
<Select.item_text>Aubergine</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
<Select.item
class="DemoSelectItem"
value="broccoli"
>
<Select.item_text>Broccoli</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
<Select.item
class="DemoSelectItem"
value="carrot"
disabled
>
<Select.item_text>Carrot</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
<Select.item
class="DemoSelectItem"
value="courgette"
>
<Select.item_text>Courgette</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
<Select.item
class="DemoSelectItem"
value="leek"
>
<Select.item_text>Leek</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
</Select.group>
<Select.separator class="DemoSelectSeparator" />
<Select.group>
<Select.label class="DemoSelectLabel">Meat</Select.label>
<Select.item
class="DemoSelectItem"
value="beef"
>
<Select.item_text>Beef</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
<Select.item
class="DemoSelectItem"
value="chicken"
>
<Select.item_text>Chicken</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
<Select.item
class="DemoSelectItem"
value="lamb"
>
<Select.item_text>Lamb</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
<Select.item
class="DemoSelectItem"
value="pork"
>
<Select.item_text>Pork</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.item_indicator>
</Select.item>
</Select.group>
</Select.viewport>
<Select.scroll_down_button class="DemoSelectScrollButton">
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z"
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</Select.scroll_down_button>
</Select.content>
</Select.root>
/* reset */
button {
all: unset;
}
.DemoSelectTrigger {
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 4px;
padding: 0 15px;
font-size: 13px;
line-height: 1;
height: 35px;
gap: 5px;
background-color: white;
color: var(--violet-11);
box-shadow: 0 2px 10px var(--black-a7);
}
.DemoSelectTrigger:hover {
background-color: var(--mauve-3);
}
.DemoSelectTrigger:focus {
box-shadow: 0 0 0 2px black;
}
.DemoSelectTrigger[data-placeholder] {
color: var(--violet-9);
}
.DemoSelectIcon {
color: Var(--violet-11);
}
.DemoSelectContent {
overflow: hidden;
background-color: white;
border-radius: 6px;
box-shadow:
0px 10px 38px -10px rgba(22, 23, 24, 0.35),
0px 10px 20px -15px rgba(22, 23, 24, 0.2);
}
.DemoSelectViewport {
padding: 5px;
}
.DemoSelectItem {
font-size: 13px;
line-height: 1;
color: var(--violet-11);
border-radius: 3px;
display: flex;
align-items: center;
height: 25px;
padding: 0 35px 0 25px;
position: relative;
user-select: none;
}
.DemoSelectItem[data-disabled] {
color: var(--mauve-8);
pointer-events: none;
}
.DemoSelectItem[data-highlighted] {
outline: none;
background-color: var(--violet-9);
color: var(--violet-1);
}
.DemoSelectLabel {
padding: 0 25px;
font-size: 12px;
line-height: 25px;
color: var(--mauve-11);
}
.DemoSelectSeparator {
height: 1px;
background-color: var(--violet-6);
margin: 5px;
}
.DemoSelectItemIndicator {
position: absolute;
left: 0;
width: 25px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.DemoSelectScrollButton {
display: flex;
align-items: center;
justify-content: center;
height: 25px;
background-color: white;
color: var(--violet-11);
cursor: default;
}
Anatomy
Import all parts and piece them together.
<Select.root id="…">
<Select.trigger>
<Select.value />
<Select.icon />
</Select.trigger>
<Select.portal id="…">
<Select.content>
<Select.scroll_up_button />
<Select.viewport>
<Select.item>
<Select.item_text />
<Select.item_indicator />
</Select.item>
<Select.group>
<Select.label />
<Select.item>…</Select.item>
</Select.group>
<Select.separator />
</Select.viewport>
<Select.scroll_down_button />
<Select.arrow />
</Select.content>
</Select.portal>
</Select.root>
Anatomy
-
Root - Contains all the parts of a select. Renders a hidden input when `name` is set.
-
Trigger - The button that toggles the select. `Select.content` positions against the trigger.
-
Value - Reflects the selected value, or shows a `placeholder` when empty.
-
Icon - A decorative affordance beside the value (often a chevron).
-
Portal - Portals content into the target (default `body`).
-
Content - The listbox that opens when the select is open.
-
Viewport - The scrolling viewport that contains all items.
-
Item - A selectable option. Requires a unique `value`.
-
Item Text - Text shown in the trigger when this item is selected.
-
Item Indicator - Renders when the item is selected.
-
Group - Groups multiple items; pair with `Select.label`.
-
Label - A label for a group (not focusable via arrow keys).
-
Separator - Visually separates items or groups.
-
Scroll Up Button - Optional affordance to scroll the viewport up.
-
Scroll Down Button - Optional affordance to scroll the viewport down.
-
Arrow - Optional arrow rendered inside `Select.content`.
API Reference
Root
Contains all the parts of a select.
Use value with on_change for controlled state in LiveView:
<Select.root id="food-select" value={@food} on_change="food_change">
…
</Select.root>
def handle_event("food_change", %{"value" => value}, socket) do
{:noreply, assign(socket, :food, value)}
end
Set name to include a hidden input for form submission.
| Prop | Type | Default | Description |
|---|---|---|---|
dir
|
string
|
"ltr"
|
|
disabled
|
boolean
|
false
|
|
id
|
string
|
—
|
|
name
|
string
|
nil
|
|
on_change
|
string
|
nil
|
Event to push when value changes. |
required
|
boolean
|
false
|
|
value
|
string
|
nil
|
The value of the select. |
dir
string
Default
"ltr"
disabled
boolean
Default
false
id
string
Default
—
name
string
Default
nil
on_change
Event to push when value changes.
string
Default
nil
required
boolean
Default
false
value
The value of the select.
string
Default
nil
Data attributes
| Attribute | Values | Description |
|---|---|---|
[data-disabled]
|
Present when disabled
|
Present when the select is disabled. |
Trigger
The button that toggles the select. Select.content positions itself against the trigger.
| Prop | Type | Default | Description |
|---|---|---|---|
disabled
|
boolean
|
false
|
|
id
|
string
|
nil
|
disabled
boolean
Default
false
id
string
Default
nil
Data attributes
| Attribute | Values | Description |
|---|---|---|
[data-state]
|
open | closed
|
Reflects whether the select is open. |
[data-disabled]
|
Present when disabled
|
Present when the select is disabled. |
[data-placeholder]
|
Present when has placeholder
|
Present when no value is selected and a placeholder is shown. |
Value
The part that reflects the selected value. By default the selected item's item_text is rendered. Pass slot content for more control. Do not style this part heavily—it affects positioning. Use placeholder when the select has no value.
| Prop | Type | Default | Description |
|---|---|---|---|
placeholder
|
string
|
nil
|
placeholder
string
Default
nil
Icon
A small icon often displayed next to the value as a visual affordance that the select can open.
| Prop | Type | Default | Description |
|---|
Portal
When used, portals the content part into the target (default body).
| Prop | Type | Default | Description |
|---|---|---|---|
id
|
string
|
—
|
|
target
|
string
|
"body"
|
id
string
Default
—
target
string
Default
"body"
Content
The component that pops out when the select is open. Set data-position="popper" for popper-style positioning (similar to Popover); omit it for the default item-aligned mode.
| Prop | Type | Default | Description |
|---|---|---|---|
id
|
string
|
nil
|
id
string
Default
nil
Data attributes
| Attribute | Values | Description |
|---|---|---|
[data-state]
|
open | closed
|
Reflects whether the select is open. |
[data-position]
|
item-aligned | popper
|
Positioning mode. Defaults to item-aligned. |
Viewport
The scrolling viewport that contains all of the items.
| Prop | Type | Default | Description |
|---|
Item
The component that contains the select items.
| Prop | Type | Default | Description |
|---|---|---|---|
disabled
|
boolean
|
false
|
|
text_value
|
string
|
nil
|
Optional text value for type-to-select. |
value
|
string
|
—
|
disabled
boolean
Default
false
text_value
Optional text value for type-to-select.
string
Default
nil
value
string
Default
—
Data attributes
| Attribute | Values | Description |
|---|---|---|
[data-state]
|
checked | unchecked
|
Reflects whether the item is selected. |
[data-highlighted]
|
Present when highlighted
|
Present when the item is highlighted. |
[data-disabled]
|
Present when disabled
|
Present when the item is disabled. |
Item Text
The textual part of the item. It should only contain the text you want in the trigger when that item is selected. Do not style this part heavily—it affects positioning.
| Prop | Type | Default | Description |
|---|
Item Indicator
Renders when the item is selected. Style this element directly, or use it as a wrapper for an icon.
| Prop | Type | Default | Description |
|---|
Scroll Up Button
An optional button used as an affordance to show viewport overflow and to scroll upwards.
| Prop | Type | Default | Description |
|---|
Scroll Down Button
An optional button used as an affordance to show viewport overflow and to scroll downwards.
| Prop | Type | Default | Description |
|---|
Group
Used to group multiple items. Use with Select.label for accessible labelling.
| Prop | Type | Default | Description |
|---|
Label
Used to render the label of a group. It won't be focusable using arrow keys.
| Prop | Type | Default | Description |
|---|
Separator
Used to visually separate items in the select.
| Prop | Type | Default | Description |
|---|
Arrow
An optional arrow element rendered inside Select.content to visually link the trigger with the content.
| Prop | Type | Default | Description |
|---|
Examples
Change the positioning mode
By default, Select positions content relative to the active item (item-aligned). For popper-style positioning similar to Popover, set data-position="popper" on Select.content:
<Select.root id="popper-select">
<Select.trigger class="DemoSelectTrigger">
<Select.value placeholder="Select a fruit…" />
<Select.icon class="DemoSelectIcon">…</Select.icon>
</Select.trigger>
<Select.portal id="popper-select-portal">
<Select.content id="popper-select-content" class="DemoSelectContent" data-position="popper">
<Select.viewport>…</Select.viewport>
</Select.content>
</Select.portal>
</Select.root>
With disabled items
Style disabled items via the data-disabled attribute:
<Select.item class="DemoSelectItem" value="carrot" disabled>
<Select.item_text>Carrot</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">…</Select.item_indicator>
</Select.item>
.DemoSelectItem[data-disabled] {
color: var(--mauve-8);
pointer-events: none;
}
With a placeholder
Use the placeholder prop on Select.value when the select has no value. The trigger gets data-placeholder for styling:
<Select.trigger class="DemoSelectTrigger" aria-label="Food">
<Select.value placeholder="Pick an option" />
<Select.icon class="DemoSelectIcon">…</Select.icon>
</Select.trigger>
.DemoSelectTrigger[data-placeholder] {
color: var(--mauve-9);
}
With separators
Use Select.separator to add a separator between items:
<Select.viewport>
<Select.item value="apple">…</Select.item>
<Select.item value="banana">…</Select.item>
<Select.separator class="DemoSelectSeparator" />
<Select.item value="blueberry">…</Select.item>
</Select.viewport>
With grouped items
Use Select.group and Select.label to group items in a section:
<Select.viewport>
<Select.group>
<Select.label class="DemoSelectLabel">Fruits</Select.label>
<Select.item value="apple">…</Select.item>
<Select.item value="banana">…</Select.item>
</Select.group>
</Select.viewport>
With complex items
Custom content inside items is supported. Use text_value on Select.item when typeahead should match something other than visible text:
<Select.item value="adolfo" text_value="Adolfo Hess">
<Select.item_text>
<img src="/avatars/adolfo.png" alt="" width="20" height="20" />
Adolfo Hess
</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">…</Select.item_indicator>
</Select.item>
Controlling the value displayed in the trigger
By default the trigger shows the selected item's item_text. For more control, use controlled value/on_change and pass slot content to Select.value:
<Select.root id="country-select" value={@country} on_change="country_change">
<Select.trigger class="DemoSelectTrigger" aria-label="Country">
<Select.value aria-label={@country}>
{Map.get(@country_flags, @country)}
</Select.value>
<Select.icon class="DemoSelectIcon">…</Select.icon>
</Select.trigger>
<Select.portal id="country-select-portal">
<Select.content id="country-select-content" class="DemoSelectContent">
<Select.viewport>
<Select.item value="france">
<Select.item_text>France</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">…</Select.item_indicator>
</Select.item>
<Select.item value="united-kingdom">
<Select.item_text>United Kingdom</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">…</Select.item_indicator>
</Select.item>
</Select.viewport>
</Select.content>
</Select.portal>
</Select.root>
def handle_event("country_change", %{"value" => value}, socket) do
{:noreply, assign(socket, :country, value)}
end
Accessibility
Adheres to the ListBox WAI-ARIA design pattern.
See the W3C Select-Only Combobox example for more information.
Labelling
Use the Label primitive to offer a visual and accessible label for the select:
<Label>
Country
<Select.root id="labelled-select">…</Select.root>
</Label>
<%!-- or --%>
<Label for="labelled-select-trigger">Country</Label>
<Select.root id="labelled-select">
<Select.trigger id="labelled-select-trigger" class="DemoSelectTrigger">…</Select.trigger>
…
</Select.root>
Keyboard Interactions
Keyboard Interactions
| Key | Description |
|---|---|
Space
|
When focus is on `Select.trigger`, opens the select and focuses the selected item. When focus is on an item, selects the focused item. |
Enter
|
When focus is on `Select.trigger`, opens the select and focuses the first item. When focus is on an item, selects the focused item. |
ArrowDown
|
When focus is on `Select.trigger`, opens the select. When focus is on an item, moves focus to the next item. |
ArrowUp
|
When focus is on `Select.trigger`, opens the select. When focus is on an item, moves focus to the previous item. |
Escape
|
Closes the select and moves focus to `Select.trigger`. |
Custom APIs
Create your own API by abstracting the primitive parts into your own component.
Abstract down to Select and SelectItem
This example abstracts most of the parts.
Usage
<.select id="my-select" value={@value} on_change="select_change">
<:item value="1">Item 1</:item>
<:item value="2">Item 2</:item>
<:item value="3">Item 3</:item>
</.select>
Implementation
attr :id, :string, required: true
attr :value, :string, default: nil
attr :on_change, :string, default: nil
attr :placeholder, :string, default: "Select…"
attr :class, :string, default: nil
slot :item, required: true do
attr :value, :string, required: true
end
def select(assigns) do
~H"""
<Select.root id={@id} value={@value} on_change={@on_change}>
<Select.trigger class={@class || "SelectTrigger"}>
<Select.value placeholder={@placeholder} />
<Select.icon class="DemoSelectIcon">
<svg width="15" height="15" viewBox="0 0 15 15" aria-hidden="true">…</svg>
</Select.icon>
</Select.trigger>
<Select.portal id={"#{@id}-portal"}>
<Select.content id={"#{@id}-content"} class="DemoSelectContent">
<Select.scroll_up_button class="DemoSelectScrollButton">…</Select.scroll_up_button>
<Select.viewport>
<Select.item :for={item <- @item} class="DemoSelectItem" value={item.value}>
<Select.item_text>{render_slot(item)}</Select.item_text>
<Select.item_indicator class="DemoSelectItemIndicator">…</Select.item_indicator>
</Select.item>
</Select.viewport>
<Select.scroll_down_button class="DemoSelectScrollButton">…</Select.scroll_down_button>
</Select.content>
</Select.portal>
</Select.root>
"""
end