Skip to main content
BEEQ Select component overviewBEEQ Select component overview

Select component overview

The select input lets users choose one option — or multiple options when multiple is enabled — from a predefined list. It pairs a styled trigger with a floating panel of bq-option elements and integrates with native HTML forms.

When to use

Use Select when
  • The option list contains more than five items and a radio group would take too much space
  • Users need to choose from a predefined set of values in a form
  • Multiple selection from a long list is required and a checkbox group would be impractical
  • The available choices are dynamic or loaded from an external source
Do not use Select when
  • Fewer than five options are available — use radio buttons or a segmented control instead
  • The user needs to type a free-form value not constrained to a fixed list — use an Input instead
  • The choices are binary (yes / no, on / off) — use a Checkbox or toggle instead

Patterns

Single select

Single Select allows the user to pick one option from a menu.

Multiple select

Multiple Select supports independent choices. Add enable-checkboxes when checkboxes make the multi-select state easier to scan. Selected items appear as dismissible tags in the input.

Nested multiple select

Use nested options for selectable categories and their related choices. Parents cascade selection to their selectable descendants and show a mixed state when only some descendants are selected.

Anatomy

BEEQ Select component anatomyBEEQ Select component anatomy

Select component anatomy

The select is composed of a label, an input field, a dropdown panel, and optional status indicators.

Design guidelines

Single vs. multiple selection

Nested multiple selection

Use nested options when people need to select a category, its related choices, or both. Nesting is available only with multiple; single selects render only top-level options and log a browser warning when nested markup is present.
  • Nested multi-select always renders checkboxes. You do not need enable-checkboxes.
  • Selecting a parent selects its selectable descendants. Selecting a child also selects its selectable ancestors.
  • A parent becomes indeterminate while only some of its selectable descendants are selected. Disabled and hidden descendants are not changed by a cascade.
  • Use a unique value for every option in the tree. Duplicate values are reported in the browser console because they make the value and event payload ambiguous.
In the input, nested selections render one tag per selected top-level parent. A partial selection adds the descendant count, such as Frontend (2); a fully selected branch keeps the parent label without a count.

Validation states

Usage

Default

Enhance user interaction by implementing the default select variant. Perfect for scenarios where you want users to choose from a list of options within a dropdown.

Open

Use the open attribute to display the select dropdown by default, offering users quick access to the available options without additional clicks. Use autofocus to keep the input focused on render.

Initial value

Set an initial value for the select component to preselect an option, providing a streamlined user experience and minimizing manual input.

Display value

The displayValue property (display-value attribute) on bq-option overrides the value shown inside the input after selection. Use it when the full option content (e.g., multi-line layout) should not be collapsed into the input field.

Disabled

Employ the disabled attribute to restrict user interaction with the select component temporarily, useful for scenarios where certain options are unavailable.

Multiple

Use multiple to enable multiple selections. Add enable-checkboxes for a flat list when visible checkboxes make the current selections easier to scan. Use keep-open-on-select to keep the panel open while the user selects options. The max-tags-visible attribute controls how many tags are shown before a +N overflow tag appears.

Nested options

Nested options are a multiple-select pattern. Put child options in their parent’s options slot and give every option a unique value. The parent below starts expanded with one selected child, so its checkbox is indeterminate and its tag shows the selected descendant count.

Set a fully selected branch

Use value to set and control the Select’s state. It stays a flat array, even when the options are nested.
1

Add the parent value

Include "frontend" when the Frontend parent is selected.
2

Add every selectable descendant

Include "frontend:react" and "frontend:vue" to mark the whole Frontend branch as selected.
The component keeps the parent tag as Frontend and shows the all-selected-label summary in the open list.
HTML
selectionTree is event output, not component input. Do not pass it back into value.
Set show-selection-summary="false" on a parent option when the visual summary beside its expand control is not useful in your context.

Read a nested selection event

Listen for bqSelect when the selection changes. The event gives you two views of the same state:
selectionTree exists only for nested multi-select. It is undefined for flat and single Selects. Treat it as read-only output; keep using value to control the component.
For example, event.detail.selectionTree looks like this after selecting the full Frontend branch and Node.js:

Options

Prefix

Use the prefix slot to include additional visual elements or context, such as an icon, to provide users with extra visual cues about the field’s purpose.

Suffix

Leverage the suffix slot to enhance the select component with extra elements. The default suffix is a rotating chevron caret; replace it with any icon to match your design context.

Validation

Use validation-status to signal the validation state (error, warning, or success) of the select component, ensuring accurate data input and real-time feedback.

Label with “Optional”

Choose this style when you want to include an optional label for additional context, providing users with guidance while maintaining a clean and unobtrusive design.

Label with “Info Tooltip”

Enhance user understanding by incorporating a bq-tooltip alongside the label, offering additional information or clarification about the select.

Panel placement

Adjust the panel placement to suit your layout. Use the placement attribute to position the dropdown relative to the input — useful when the select is near the bottom of the viewport.

Visually hidden label

Keep an accessible label when the layout does not need one visibly. Use visually hidden text in the label slot, for example in a toolbar or compact table.

With no Helper Text

Select this variant when you don’t need additional helper text, giving users a straightforward and uncluttered experience.

Non-searchable

Set disable-search to keep the option panel usable while blocking any typing in the input. Users can still open the panel, browse options, and pick a value, but they cannot filter the list by typing. Use it when the option set is short enough to scan and you want to prevent free-text input from shrinking the visible options. Keyboard focus alone keeps the panel closed. Press ArrowDown or ArrowUp to open it and move to an option.
The readonly attribute currently has the same effect and is now deprecated — use disable-search instead. In a future major release, readonly will prevent changing the selected value rather than disabling filtering.

Without clear button

Set disable-clear to hide the clear button and prevent one-click deselection. Use this when selecting a value is required and removing it should not be trivially easy.

Custom behavior

Call preventDefault() on the bqInput event to take full control of option filtering. This lets you implement async filtering, loading states, empty-state messages, and matched-text highlighting entirely in your own code. Keyboard focus alone keeps the panel closed. With search enabled, typing opens it after the debounced bqInput event. Calling preventDefault() on that event stops automatic opening as well as built-in filtering. Set open yourself when your custom flow needs to show the panel. Adding, replacing, or removing options preserves the text you are typing in both single- and multi-select. The search text is separate from value, which holds the committed selection. Selecting an option, clearing or resetting the field, or changing value programmatically replaces the search text with the selected label or an empty input. In a searchable single-select, deleting all text or leaving only whitespace immediately clears the committed value and deselects the option. If a selection existed, this emits bqClear, even when your handler cancels bqInput. debounce-time delays the search event, not selection clearing. Editing a nonempty search draft keeps the previous selection; deleting a multi-select search draft keeps its selected tags. For a controlled Select, handle bqClear to clear the selected value in your application or form state and reset the async query. Use the same handler for the clear button and text deletion. To show all options without removing a single selection, open the panel while keeping its selected label visible. Use reset(select.value) to cancel a search draft and restore that label. For async results, keep selected options in the list when you need their labels or tags to remain visible. Ignore stale responses in your request handler so an older search does not replace newer results. bqFocus and bqBlur track focus entering and leaving the whole Select. Moving between the input, options, and internal controls does not emit these events. Use bqBlur for field-level cleanup and call reset(select.value) to clear the search text without clearing the selection or remounting the control. These events no longer follow input-only focus changes. If your handler needs to respond when the native input itself gains or loses focus, listen to that input’s native focus or blur event instead.
Set debounce-time to throttle how often bqInput fires while the user types — 300 ms is a good default for async requests.

Form integration

bq-select is form-associated, so it participates in native submission, reset, and required validation. Use name for the submitted key, required for browser validation, and form-validation-message for a custom native validation message.
The live preview runs in an iframe sandbox that does not allow native form submission. Its Submit button uses type="button" and bqClick to validate the form and display FormData without submitting it. The copyable examples use native type="submit" buttons for normal application forms.

Best practices

DoAlways populate the label slot so the select has an accessible name visible to all users, not just those relying on assistive technology.
Don’tLeave the label slot empty and rely solely on placeholder text — placeholder disappears once a value is selected and is not announced reliably by screen readers.
DoWrite a clear, neutral placeholder such as “Choose a country” so users understand the field is interactive and what kind of value they are selecting.
Don’tUse the placeholder as a label substitute or write a directive like “Select” without context — this gives users no information about what the field is for.
DoUse validation-status and the helper-text slot together when the field is required or has a validation error, so users understand exactly what is wrong and how to fix it.
Don’tApply validation-status="error" without a helper text message — a red border alone does not tell users what is wrong or how to correct it.
DoProvide meaningful option labels that describe the value clearly — e.g., “Europe” instead of “EU”, “Pro plan ($49/mo)” instead of “pro”.
Don’tUse internal codes, abbreviations, or IDs as the visible option labels — what is meaningful in your database is not necessarily meaningful to users.

Accessibility

  • Built in: the native <input> inside the control has role="combobox", aria-haspopup ("listbox" for flat options or "tree" for nested options), aria-expanded (toggled when the panel opens and closes), and aria-controls pointing to the options container by id.
  • Built in: aria-disabled="true" is applied on the input when disabled is set.
  • Built in: the component is form-associated via ElementInternals, so it works with native <form> elements, required validation, and formResetCallback.
  • Built in: keyboard navigation — Tab moves focus to the input without opening the panel; ArrowDown / ArrowUp open the panel and move focus to the first or last option; while the panel is open, ArrowDown / ArrowUp move between options and Home / End jump to the first or last option; Enter or Space selects the focused option; Escape closes the panel and returns focus to the input without changing the selection.
  • Built in: in multi-select mode, Backspace on an empty input removes the most recently added tag.
  • Built in: opening the panel closes any other open select, date picker, or dropdown panel, so only one panel stays open at a time.
  • Built in: nested multi-select changes the option container to a tree. ArrowRight expands a branch or moves to its first child; ArrowLeft collapses a branch or returns to its parent. Enter and Space select the focused option without changing expansion.
  • Built in: parent tree items expose checked, unchecked, or mixed state as their descendants are selected, and aria-expanded communicates whether a branch is open. The separate visual expand button is hidden from assistive technology and removed from the Tab order; keyboard users expand and collapse the focused tree item with the arrow keys.
  • Your responsibility: provide text in the label slot. The component associates its native <label> with the combobox input through for and id. Use visually hidden text when the layout cannot show a visible label.
  • Your responsibility: when validation-status is "error", populate the helper-text slot with a clear error description. The component does not set aria-describedby or aria-invalid on the input, so do not rely on color alone to communicate the error.
  • Your responsibility: if options are loaded asynchronously, show a disabled loading option in the default slot, as in the custom filtering example. panel is a shadow part for styling, not a slot.
  • Your responsibility: give every nested option a unique value. Values are used for selection state, form submission, tags, and the selectionTree event detail.
The internal label currently sets aria-label from the technical name value, or "select" when no name is supplied. This may override the slotted label in accessible-name computation and is tracked in the Select follow-up issue. Provide meaningful label text and verify the computed combobox name in your supported browsers and assistive technologies.

API reference

Properties

Methods

Events

Slots

Shadow parts

CSS custom properties

Resources

Interactive playground

Explore Select variants and states in Storybook

Source code

View the component source on GitHub