Select
Lets a user choose one option from a list in a popup.
Show codeHide code
Installation
import {
Select,
SelectContent,
SelectItem,
SelectLabel,
SelectList,
SelectTrigger,
} from "@makeplane/propel/components/select";Usage
import {
Select,
SelectContent,
SelectItem,
SelectLabel,
SelectList,
SelectTrigger,
} from "@makeplane/propel/components/select";
import { SelectField } from "@makeplane/propel/elements/select";
const SERVER_TYPES = [
{ label: "General purpose", value: "general" },
{ label: "Compute optimized", value: "compute" },
{ label: "Memory optimized", value: "memory" },
];
export default function BasicDemo() {
return (
<Select items={SERVER_TYPES} defaultValue="general">
<SelectField>
<SelectLabel size="lg">Server type</SelectLabel>
<SelectTrigger size="lg" />
</SelectField>
<SelectContent>
<SelectList>
{SERVER_TYPES.map(({ label, value }) => (
<SelectItem key={value} value={value} label={label} />
))}
</SelectList>
</SelectContent>
</Select>
);
}Examples
Placeholder
Without a defaultValue, the trigger shows its placeholder until an option is picked.
Show codeHide code
Multiple
Show codeHide code
Select all and footer
SelectContent pins sticky content above and below the scrolling list: header and footer. For a
multiple select, put a SelectSelectAll row (and a SelectSeparator) in a SelectStickyList in
the header, wired to the controlled value: it shows a dash while only some options are picked.
A SelectFooter in the footer holds a hint or a count.
Show codeHide code
Grouped
Group related options under a SelectGroup with a SelectGroupLabel, and divide the groups with a
SelectSeparator. The label’s meta pins content at its end, such as a count; it is left out of
the group’s accessible name.
Show codeHide code
Option details
secondaryText adds muted text after an option’s label, and description a muted second line (the
row grows to fit it). trailing pins content at the row’s end, after the check. The trigger shows
the label alone.
Show codeHide code
Popup width
SelectContent matches the trigger’s width by default (sizing="anchor"). sizing="auto" sizes
the popup to its options instead, between 204px and 400px, for a trigger whose options need more (or
less) room than it has.
Trigger width
Field-style triggers use width="auto" by default, preserving the existing 288px minimum while
allowing the trigger to grow. Use width="fit" when a secondary or ghost trigger should hug
its rendered icon, value, chevron, gaps, and padding. This does not change SelectContent sizing;
an anchor-sized popup can still grow wider than a compact trigger when its options need more room.
The pill variant is already intrinsically sized and does not accept width.
Show codeHide code
Sizes
For secondary and ghost, size runs md 28 / lg 32 / xl 36 / 2xl 40 px. Pass the same value to SelectLabel
and SelectTrigger: the label tracks the trigger’s rung, while the option rows stay one fixed 32px
height at every size, like Combobox and Menu rows. The label runs 13px at md and lg, 14px at
xl and 2xl — matching what a field wrapper renders at the same control height, so it sits one
step calmer than the value at lg and 2xl and matches it at xl. See
Sizing for the full ladder.
Show codeHide code
Variants
SelectTrigger’s secondary variant (default) is the bordered field surface; ghost drops the
border and fill; pill keeps bordered chrome but hugs its value for compact property and filter
selectors. Like Propel Pill, the pill trigger has no automatic chevron: an open popup is conveyed
by its active chrome. Pair it with SelectContent sizing="auto" so its options are not constrained
by the compact anchor width. Its size scale is xs, sm, and md, matching Propel Pill; the type
contract rejects those compact sizes on secondary/ghost and rejects lg–2xl on pill.
Show codeHide code
With icon
SelectTrigger’s icon prop shows a leading glyph before the value, via the shared Icon slot.
Show codeHide code
Option icons
SelectItem’s icon prop shows a leading glyph before an option’s label. In a multiple select
it follows the checkbox.
Show codeHide code
API Reference
Select
The select Root — Base UI's context/state provider (renders no element of its own). A
behavior-only role, so it lives in components (rules 1a, 2); the styled parts live in
elements/select and are grafted onto Base UI behavior here. It also shares multiple with its
SelectItems, which switch to a leading checkbox marker for a multi-select.
SelectTrigger
The ready-made select trigger: grafts Base UI's Select.Trigger behavior onto the styled trigger
and bakes its anatomy — an optional leading icon, the selected value (Base UI's Select.Value
grafted onto SelectValue), and an owned chevron indicator for field-style variants. The pill
variant follows Propel Pill anatomy and has no baked trailing icon; its open state is conveyed by
active chrome. Children are baked, so the prop is omitted.
SelectContent
The select list surface: Base UI portal + positioner, with the popup grafted onto the shared
OverlayPanel — the same elevated card, radius, open/close animation and anchor width as the
Combobox and Menu panels, including the ScrollArea its list scrolls in.
SelectLabel
The ready-made select label: grafts Base UI's Select.Label behavior — automatic association
with the trigger — onto the styled label.
SelectList
The container for the select items: Base UI's Select.List grafted onto the styled SelectList,
the padded list inside the SelectContent panel (the rows are SelectItems).
SelectItem
The ready-made select option row: grafts Base UI's Select.Item behavior onto the styled
SelectItem row — the shared menu-row chrome, so the list matches Combobox and Menu — and lays
out an optional leading icon, the label with any inline secondary text, an optional description
line, the selection marker, and trailing content. A single-select row trails its label with a
check (SelectItemIndicator); inside a multiple select the row leads with a checkbox box
(SelectCheckboxItemIndicator), matching a multi-select Combobox. Both markers are
keepMounted: the check so its gutter holds while unselected (a selection change never
re-truncates a label), the checkbox so the empty box shows. Only the label goes through Base
UI's Select.ItemText, so it alone is what the trigger shows for the selected option.
SelectGroup
Groups related options under a SelectGroupLabel.
SelectGroupLabel
The muted heading naming a SelectGroup, with optional meta pinned at its end. Base UI's
GroupLabel behavior is grafted onto the heading's TITLE, not the whole row, so the group's
accessible name is the heading text alone — a count or a "Clear" button in meta stays out of
it. Place it inside the SelectGroup it names, so Base UI wires it as that group's label.
SelectSeparator
A thin divider between groups of options — the same chrome as ComboboxSeparator and
MenuSeparator. Purely visual, so it is aria-hidden and carries no separator role: a listbox
may only own options and groups, so Base UI's Select.Separator (which renders
role="separator") would be invalid ARIA inside the list. The SelectGroup labels already give
a screen reader the structure the line shows.
SelectSelectAll
Sticky "select every option" row for a multiple select, the counterpart of ComboboxSelectAll.
Not a SelectItem — it is not an option — so compose it in SelectContent's header (inside a
SelectStickyList) above the scrolling list, and wire checked / indeterminate / onClick to
the select's controlled value.
SelectStickyList
Pads sticky rows in SelectContent's header (a SelectSelectAll + SelectSeparator) with the
same inset the scrolling list uses.
SelectFooter
A non-interactive footer pinned below the list (a hint, a count) — the same chrome as
MenuFooter. Pass it through SelectContent's footer.