Pickers

Combo Box

A text input combined with a selectable list.

Rust
1use herogpui::components::combo_box::ComboBox;

Usage

Values and options use 14px text with 20px lines. Section headers use 12px text with 16px lines and keep their own spacing. The popup flips near window edges and scrolls to keep options reachable in short windows.

Rust
1use herogpui::prelude::ComboBox;
2
3ComboBox::new(
4    self.combo_state.clone(),
5    language_items(),
6)
7.label("Language")
8.placeholder("Pick or type")
9.is_open(is_open)
10.on_open_change(bool_cb(cx.listener(|this, open: &bool, _, cx| {
11    this.combo_open = *open;
12    cx.notify();
13})))
14.on_selection_change(cx.listener(|this, _key: &SharedString, _, cx| {
15    this.combo_open = false;
16    cx.notify();
17}))

Live HeroGPUI compiled to WebAssembly. Select any example without loading another WASM instance.

Anatomy

Rust types

ComboBox

Combo Box is assembled from these builders. The API reference lists each one.

Customization

Appearance builders and theme tokens Combo Box uses.

Styling

Combo Box styling
StyleDescription
ComboBox text_size + line_height + section header stylesText keeps its line height under surrounding styles. Section headers have 8px horizontal, 6px top and 4px bottom padding; selecting an option does not add font weight.
flex_col + gap(px(4.)) + min_w(px(180.))Root field stack; matches the 4px gap but also enforces a local 180px minimum when not full width.
is_invalid/error_message branchInvalid fields hide the description slot so the error message can occupy the message position.
Input::new + end_content(trigger) + Input focus stylesThe input expands beside the trigger and uses the field focus treatment; delegates the inner field to Input.
input_group.relative + Input end_contentInput and trigger positioning context; provides a relative group but not the full isolation contract.
value_content(SelectionValue) + FIELD_TEXT + field.foregroundSelected-value row typography and placeholder treatment; supplies equivalent values only through value_content.
trigger flex + size(px(20.)) + muted + end_contentChevron trigger positioning and chrome; draws a 20px local hit surface through Input's end content.
trigger.hover + interaction gatesHas hover and disabled interaction gates, but no separate trigger focus ring and no pressed opacity treatment.
svg size(FIELD_ICON) + CHEVRON_DOWNUses a 16px chevron, but it does not rotate the icon from closed to open.
scrollable_field_popover + max_h_full (plain) / Infer (virtual) + occlude + p(px(4.)) + overlay bg/shadowAnchored to the field with an 8px gap, flipping to the side with more room when the preferred side cannot fit, keeping a 12px cross-axis viewport inset with the scroller capped to the available height; plain and virtual lists scroll within the available height and wheel input stays in the panel. Uses its own 4px panel inset and exact trigger width rather than upstream p-0 and min-width.
anim::entering_zoom + Motion::LIST_INMatches fade, 95% start, duration and curve through metric animation, but has no placement-specific slide.
panel p(px(4.)) + row px(8.)/py(6.)Listbox inset; places 4px around the outer panel and 8px/6px on each row instead.
row px(px(8.)) + indicator(render)The local rows have 8px horizontal padding and draw a tick for multiple selections only.
full_width(true) + root/input w_fullFull-width root and input group.

API reference

Builders

Combo Box builders
BuilderTypeDefaultDescription
input_value(value, cx)value, cxCurrent text in the input (controlled).
default_input_value(text)textInitial text in the input (uncontrolled).
on_input_change(callback)Fn(value: string) -> ()Called when the input text changes.
is_open(bool)boolControlled open state inherited from the ComboBox primitive.
default_open(bool)boolfalseInitial open state for the uncontrolled popover.
on_open_change(callback)Fn(isOpen: bool) -> ()Called when focus, typing, the trigger, Escape, selection or outside press changes the open state.
selection_mode(SelectionMode)SelectionModeSelectionMode::SingleWhether one or multiple options may be selected.
selected_key(key, cx)key, cxControlled single-selection item key, with v3's null spelled as the empty string; writes the key's label through the input state and hands the key to the controlled selection.
default_value(keys)Key | nullUncontrolled single-selection seed addressed by item key; default_value takes an ordered key list rather than a nullable key.
on_selection_change(callback) / on_selection_change_all(callback)Fn(key: Key | null) -> ()Reports the picked key; the null a cleared input or a committed custom value stands for is spelled through on_selection_change_all's empty slice — reported only when a previous selection existed — the port's rendering of the primitive's null.
selected_keys(keys)Key | null | Key[]Controlled selection as ordered item keys for both single and multiple modes; v3's null is the empty list, and a single-mode selection keeps the first listed key.
new(state, items)state, itemsOptions displayed in the listbox as keyed PickerItems: the stable key the selection, disabledKeys and form value address, and the label the filtering, input text and rendering use.
disabled_keys(keys)iterable of keysKeys of the options that remain visible but cannot be selected; the state is per key, so one of two same-label items can be disabled alone.
filter(callback)Fn(text: string, inputValue: string) -> boolCustom option matcher; the default is the language-sensitive useFilter base contains matcher, while the port's built-in matcher is case-insensitive substring and its filter callback is the direct companion seam.
is_disabled(bool)boolfalseDisables the input, trigger, keyboard navigation and popover.
is_read_only(bool)boolfalseKeeps the field readable and focusable while preventing edits and opening.
is_required(bool)boolfalseMarks the field as required; carries the flag to its field and validation surface, without browser constraint-validation semantics.
is_invalid(bool)boolfalseMarks the field invalid and switches the description slot to the error message.
validate(callback)Fn(value: ComboBoxValidationValue) -> ValidationError | true | null | undefinedRuns validation against the input text and surfaces an error, but does not pass the browser primitive's selectedKey-shaped validation value or full return union.
validation_behavior(ValidationBehavior)ValidationBehaviorValidationBehavior::NativeSelects the local validation behavior carried by the field; native form blocking and ARIA attributes are not browser semantics in GPUI.
name(value) + form_field()value) + form_field(Names the local FormField whose live value follows formValue; the input keeps its implicit-Enter submission, but there is no automatic form boundary.
form_value(ComboBoxFormValue)ComboBoxFormValueComboBoxFormValue::KeyChooses what a named field submits: the selected key(s) by default, the typed text under allowsCustomValue (pinned Components 1.20.0 forces text there). The serialization rides the local FormField; there is no browser form boundary.
auto_focus(bool)boolfalseFocuses the input on its first render.
allows_custom_value(bool)boolfalseAllows Enter to commit text that is not in the collection; the committed custom value keeps the typed text, carries a null selected key, and forces formValue to text as pinned does.
allows_empty_collection(bool)boolfalseKeeps the popover open when filtering produces no rows.
menu_trigger(MenuTrigger)MenuTriggerMenuTrigger::FocusChooses whether focus, input, or only an explicit trigger/key action opens the list.
should_focus_wrap(bool)boolfalseWraps keyboard navigation from the last option to the first and back.
full_width(bool)boolfalseMakes the field and its input group fill the available width.
variant(FieldVariant)FieldVariantFieldVariant::PrimaryVisual field treatment for the input group.
value_content(render)AnyElement | Fn(values: ComboBoxValueRenderProps) -> AnyElementCustom selected-value content; the local closure receives SelectionValue rather than the browser state object.
placement(Placement)PlacementPlacement::BottomEight cardinal/start/end placements are available; RAC's full 22-value placement union is not. The panel anchors to the measured field with an 8px gap and flips when the preferred side cannot fit and the opposite side has more room.

Parts

Combo Box parts
PartDescription
ComboBoxField root owning input text, selection, filtering, validation, open state and the floating list.
ComboBoxInput and trigger group; renders this as a built-in field composition rather than a child slot.
ComboBoxOptional selected-value row with SelectionValue content; the browser placeholder and state object are not separate builders.
ComboBoxBuilt-in chevron hit surface that toggles the list and focuses the input; it is not a separate browser Button element.
ComboBoxPlacement-aware floating list surface with outside-press and Escape dismissal; arbitrary popover children are not composable.

States

Combo Box states
BuilderStateDescription
open_state + overlay_active + show_listOpenThe input group owns a live filtered list when the menu is open.
InputState::focus_handle + MenuTrigger::FocusFocusedInput focus drives the field's focused treatment and the default focus menu trigger.
trigger.hover(default.hover())Hovered triggerThe trigger responds to hover, but the local hover fill is broader than v3's trigger text-color change.
trigger_pressed capture tokenPressed triggerThe local trigger captures the press for dismissal ordering but does not reproduce v3's pressed opacity surface.
is_disabled + interaction gatesDisabledInput and trigger are inert and the popover cannot open; browser disabled attributes and root data state are unavailable.
is_read_only + interaction gatesRead onlyThe input remains readable while edits, opening and selection changes are gated.
is_invalid/error_message + Input invalidInvalidError content replaces the description and the field is styled invalid; GPUI has no matching attributes.
is_required + form_fieldRequiredThe required flag reaches the local field and validator, without browser required/form semantics.
filter/default substring + max_itemsFilteredRows are limited by the current input and optional custom matcher; the locale-sensitive useFilter default is approximated by case-insensitive substring matching.
allows_empty_collection + empty messageEmpty collectionAllowsEmptyCollection keeps the panel open and draws an explicit no-match or custom-value message.
disabled_keys + disabled opacity gateDisabled optionDisabled labels remain visible and cannot be activated, with local muted opacity in place of browser collection semantics.
selected_keys + indicator(render)Selected optionSelected labels are tracked and multiple selection can draw an indicator; generic ListBox item data attributes are not emitted.
selection_mode(Multiple) + on_selection_change_allMultiple selectionRows toggle membership without closing and ComboBox.Value can receive the selected labels.
allows_custom_value + Enter activationCustom valueEnter commits unmatched input and reports it through the local selection callback.
menu_trigger(MenuTrigger)Menu triggerFocus, input and manual opening modes match the documented trigger choices, including arrow-key opening.
should_focus_wrap + list_nav::resolveFocus wrapKeyboard navigation can wrap between the first and last enabled rows.
anim::entering_zoom + Motion::LIST_INEnteringThe list fades and grows from 95% over 150ms with the list curve; placement-specific slide translations are not reproduced.