@gridui/select
Core family. Public API: Combobox, ComboboxProps, MultiSelect, MultiSelectProps, Select, SelectAction, SelectGroup, SelectOption, SelectProps.
Import components from @gridui/select and load @gridui/select/styles.css after Theme.
Load styles for any composed dependency used by this family. Applications own routes, labels, state persistence, and branding.
See component ownership and release history.
Select and Combobox
Select and Combobox share options, actions, selection state, form integration,
and the dropdown surface, highlight, indicators, typography, and motion.
Radix Select owns option navigation; Combobox combines Radix Popover with a
native editable input and an active-descendant listbox.
Each popup contains an option listbox and a separate action footer; buttons and
links never masquerade as options. Select defaults to position="popper", opening
below the trigger and flipping at viewport edges. position="item-aligned" places
the selected item over the trigger when space permits, with a viewport-safe
fallback. Both modes match the trigger's outer width and limit height to available
space. The option list scrolls independently of actions. Standard and small
triggers retain 2.75rem and 2.25rem minimum heights. Option labels and descriptions wrap;
the editable input displays one line. portalContainer can keep popups within an
enclosing overlay's ownership and focus boundary. Inside a Modal or Drawer,
use portalContainer when the popup must inherit a local theme or custom portal
location. Radix layers coordinate nested focus and Escape handling. Attach a
custom container to the panel, not its scrolling body, to avoid clipping menus.
options accepts SelectOption entries or SelectGroup entries with label
and options. Option values must be unique across groups; group labels must be
unique. Each option has a string label, optional string description, decorative
icon React node, and disabled. Descriptions wrap below labels. Select shows the label and icon in its trigger;
Combobox shows the label in its input. Do not put interactive content in icons.
Use value / onValueChange for controlled state or defaultValue for local
state. The default value is empty; no option is implicitly selected. A selected
value missing from options displays its raw value and remains in native form
submission until its matching option arrives. An explicit
empty-valued option is supported for filters such as “All statuses”. Otherwise,
placeholder supplies empty-state text. Provide a label through Label.htmlFor
and id, or an accessible name on the trigger. Select forwards button props and
refs; Combobox forwards input props and refs.
For both components, name, form, required, and disabled configure the native hidden
select used for form submission and constraints. Invalid controls focus the
visible trigger. Native form reset restores initial values; controlled callers
receive resets through onValueChange, including while disabled. A cancelled reset
has no effect. Setting disabled on an open control closes its popup;
re-enabling it leaves the popup closed. Combobox also closes and clears its
search when it becomes read-only; making it editable again does not reopen it. Selections and actions also check inherited
fieldset availability at activation time, rejecting stale portalled interactions.
Supplying a localized clearLabel adds a separate clear button. It emits the
empty string and returns focus to the trigger. Clearing is disabled for required,
disabled, or already-empty controls.
Combobox also disables clearing while readOnly; external controlled updates and
native form reset retain their normal behavior.
Actions
Both components accept actions: readonly SelectAction[]. Each action has a
unique id, label, optional decorative icon, and optional disabled.
Provide either href (with optional target and rel) for a native link or
onAction for a button callback. Actions never update the selected value or emit
onValueChange. They remain available when there
are no matching options. No implicit option creation takes place.
Actions close the popup by default; closeOnAction: false keeps it open.
Closing restores trigger focus before the callback runs, allowing a callback
to open a dialog or focus another control without later focus theft. Links keep
native navigation and modified-click behavior. Disabled actions are skipped in
the Tab sequence. Actions are outside the option listbox and cannot be reached
or selected with option-navigation keys.
Search and keyboard interaction
Combobox searches directly in its editable trigger. Supply localized
searchLabel and emptyLabel strings. A visible Label associated through id
or an explicit accessible name takes precedence over the search-label fallback.
Search filters labels and descriptions using case-insensitive, NFKC-normalized
substring matching. Empty groups disappear; disabled matches remain visible.
Typing does not commit a selection. Selecting commits the label to the input;
Escape, outside dismissal, or closing actions restore the selected label.
Actions that keep the popup open preserve the current query.
Select opens with Enter, Space, or arrow keys and focuses the selected option.
Up/Down navigate enabled options; Home/End and timed character-prefix navigation
are also supported. Enter or Space commits. Combobox preserves native editing
and IME composition; typing or its arrow button opens the list, Up/Down move the
active option, and Enter commits enabled options. GridUI allows highlighting
disabled Combobox options for discovery; activating them never commits.
Its input retains DOM focus and exposes the
active option through aria-activedescendant. If options change during search,
the highlight stays on its current result when present, otherwise it falls back
to the first current result. Enter always uses the current result collection. Escape dismisses without committing
and returns focus to the trigger, leaving an enclosing owner open. Tab/Shift+Tab
reach independent actions; disabled actions are skipped. Pointer dismissal leaves
focus with the outside target.
Server rendering
Selected labels, icons, and native form values are rendered explicitly during
SSR, including grouped options and defaultValue. The editable trigger renders
its selected label before hydration; a closed popup does not render options or
execute actions. The client hydrates before popup interaction;
keep server and client initial values/options identical. Browser verification
hydrates real Node-rendered HTML and exercises selection, reset, filtering,
independent actions, focus handoff, and empty results across the viewport matrix.
Remote search and implicit option creation are not included. Use MultiSelect for multiple selection.
Implementation and lifecycle
Select uses Radix Select; Combobox composes Radix Popover with a native input, sharing the GridUI
surface, typography, spacing, and highlights. Select defaults to position="popper";
position="item-aligned" aligns the selected item with the trigger when space permits.
Both modes use the trigger width, including borders and padding.
src/select/select.tsx and src/combobox/combobox.tsx own their trigger and popup
compositions; src/shared/ owns their shared selection, form, action, and search collaborators. Their option renderers stay separate because Select owns item-text
alignment while Combobox owns a filtered collection. Radix owns positioning and
dismissal. Select delegates option navigation to Radix; Combobox owns its
editable input and active-descendant keyboard model. use-selection-value.ts
owns controlled/local selection and initial-value reset. use-selection-popup.ts
owns availability, open/close transitions, and focus handoff. Combobox and
MultiSelect share query, current-result highlighting, and keyboard movement through
use-search-options.ts; option replacement never leaves an invalid active item. selection-form.tsx owns native form association, validation, and cancellable
reset observation. selection-actions.tsx renders independent links and buttons.
types.ts owns shared option, action, and selection contracts; options.ts owns
pure filtering and flattening; style.ts owns selection-specific presentation and composes Overlay surface
styles without translating variables into DropdownMenu names. DropdownMenu still
owns the public option-row style. selection-popup.tsx shares Combobox/MultiSelect
portal geometry, explicit anchor containment, and focus/escape event plumbing;
callers choose initial focus and dismissal policy. MultiSelect uses Form's
ListFieldControl with useListField for the native multiple-value bridge. Internal parts
are not package exports. See the selection contract
for keyboard, form, server-rendering, and focus guarantees protected by browser tests.
Surface motion uses @gridui/overlay; load @gridui/overlay/styles.css after Theme and before this family.