Skip to content

BbSelect

A searchable form field for choosing one or many values from local or fetched options.

import { BbSelect } from 'bitboss-ui';

On this page

Use it for

Use BbSelect when a form needs a searchable choice from known values, such as an assignee, city, plan or set of labels. It owns the label, validation, selection display and options panel.

Use something else when

  • BbRadioGroup: a short exclusive choice should stay visible
  • BbCheckboxGroup: a short multiple choice should stay visible
  • BbTag: users invent values instead of choosing from a known set
  • BbDropdown: the rows run actions
  • BbSelectPopover: the trigger is a pill, badge or table cell instead of a form field

Also see: Options and items, Fetching data, Coherence, Validated forms.

The field props follow BbTextInput. This page focuses on option data, search and selection.

Passthrough

Hover or tap a part to outline it. The toggles turn on loading, errors and warnings where the component has them; only parts get a row.

HighGuidance while the control is focused.

Pick from application data

Pass your API rows as they are and name the label and the stored value with item-text and item-value, each a field path or a function. Rows that already have label and value (or text and value) need neither.

Amara Okonkwo

Model: 4 (the id, not the person)

Without item-text an option of any other shape reads as JSON.stringify(item), and without item-value the model holds the whole object. Disable single options with selectable (a disabled field on the item is plain data).

For a list of strings, label, v-model and items are enough.

Choose a priority

Model: null

Keep label meaningful even with hide-label; it is the accessible name. Add clearable when the value may be emptied: clicking the selected option again keeps it.

Coming from v2

In single mode, clicking the selected option now keeps it selected and closes the panel; v2 emitted null. Multiple mode still toggles the option.

Form state and validation

Add guidance with description and hint, and let people empty the field with clearable.

TeamYearly billing saves 20%.

Inside a BbForm, the select joins the form by name and checks its rules when focus leaves both the field and its panel. Press Save billing details with no country to see it.

Required for invoicing
Choose a VAT schemePick a billing country first.
USD
This account is billed in EUR. USD invoices are converted at payment.

Nothing saved yet.

Errors make the field invalid; warnings keep it valid. Outside a form, rules do nothing: pass your own messages to errors, and compute them on the inactive event, not on blur. Add formless to keep a filter inside a form out of its data.

To require a choice on a select joined to a form, write rules="required", as the demo above does, or require the key in the form's schema. The rule blocks the submit, and the asterisk follows (Validated forms). The required prop there is a hint: it draws the asterisk and blocks nothing, for a form the server checks. Outside a form it also marks the field but blocks nothing, so pair it with your own errors.

Coming from v2

bitboss-ui/validated is gone: import BbSelect from bitboss-ui, and it joins the enclosing BbForm by itself. validate-on is now validation-mode (delete it for the old default) and vid is validation-label or name. An empty selection omits the form field; v2 posted the string "null". showChevron and #chevron are removed: use append:icon.

Multiple selection

Add multiple and seed the model as an array (any other initial shape throws); the placeholder shows while it is empty.

MilanBerlin

2 of 3 chosen

max disables new choices at the limit while selected options remain removable. When the chips no longer fit, the whole row turns into a count such as “3 hubs selected”; max-selected-labels sets that point by number instead. Pick a third hub to see it.

comma prints the selection as text, with no per-value close buttons.

Ingrid HalvorsenNikhil Raghavan

Use the chip slot when each selected value needs its own colour, avatar or remove control.

accessibility performance

Search matches the display text. Set filter-by to search other fields instead; every listed path must exist on every item.

Search a destinationSearch by city or by country.

An array of fewer than six options opens without a search field, unless you listen to option:add; a provider always keeps it. disable-writing hides it on any list, or on one platform only ('mobile').

Listen to option:add to let people create a value: Enter on a query with no highlighted option emits the trimmed text. Create the record, append it to items and select it in your handler, and ignore an empty string.

accessibility
Coming from v2allowWriting → disableWriting

filter-by accepts string[] or false (no function, no bare string). disable-writing replaces and inverts allow-writing; it also disables filtering and option:add.

Fetching options

Pass a provider to items for large lists. It receives one object, { query, reason, modelValue, signal }; on the calls the component makes by itself (every reason but 'search'), return the selected rows too, so their labels stay resolvable.

Sofia MarchettiType at least two letters to search the directory.

The component debounces typing, discards stale responses and shows its own loading state. By default it still filters the rows you return by the typed text; a server that matches the query itself (on the email above, or without accents) needs :filter-by="false", so its answer is the list. Then apply the query on every call, not only on 'search'. Set empty-text for the no-results line. Use stash only when selections must survive later result sets.

Coming from v2

prefill="focus" is now 'interaction'. prefill: false now means search-first; remove it to load on first open. no-data-text and #no-data are empty-text and #empty. Coherence also runs on the first load in v3.

null can represent an “All” option, but a provider then needs prefill: true because null also means empty. Use a sentinel such as 'all' when clearing, pruning and selecting everything must remain distinct.

All locations

Filtering by: All locations

Dependent fields

List upstream values in dependencies, then enable enforce-coherence to remove a choice the refreshed options no longer contain.

Italy
Milan

Model: milan

Multiple mode keeps the choices that are still valid instead of clearing the whole array. Switch the project to Mobile app and watch Mei Lin's chip go.

Web app
Amara OkonkwoMei Lin ChenOnly people on the project's teams can review.

With enforce-coherence, a provider that leaves the selection out of a call whose reason is not 'search' prunes it. stash does not stand in for it: enforcement checks the fetched rows alone. A provider that fails must throw, because returning [] counts as an empty result and prunes every pick.

Option rows and groups

Add a second line and an icon to each row with item-props: description, prepend:icon and append:icon, each a field path or a function of the item.

Lukas Brandt

A description makes every row two lines tall and is matched by the search. None of them appears in the closed select or in the chips.

Use group-by and the group slot for sections. Rows with no group (null or undefined) are listed first, before every header, so they never read as part of the group above.

Pick a teammate

Use the option slot for anything richer. Match taller markup with item-height and header-height (heights set only in CSS break virtual scrolling).

Amara OkonkwoAmara Okonkwo

Grouped lists virtualize by group, so do not group a list whose single group would hold thousands of rows: leave it flat or split it.

When the same rows, accessors and option row repeat on many pages, make them a preset: one CustomerSelect instead of the same BbSelect everywhere.

Panel actions and the phone sheet

Use the footer slot for actions over the model, such as select all, clear and done.

Email

Use header for context above search. Derive summaries from your model rather than selectedOptions, which is empty until provider data loads.

LisbonPorto

On a phone the panel opens as a bottom sheet, with the same header and footer; here disable-writing="mobile" drops the search on the phone only. Narrow the window to see it.

Processing

Set adaptive only to override the global choice. Use off-canvas-props for sheet behaviour and pt:sheet for sheet-only styling.

Coming from v2

header and footer replace options:prepend, options:append and their :outer variants. #prepend-outer and #append-outer are #prepend:outer and #append:outer.

Sizing and styling

The select fills its container, and the panel takes the field's width. compact lowers the field and the rows; layout puts the label beside the field.

Regional settings

Italian
Rome (CET)
Monday

layout is ignored when label-mode is floating or inside.

Pick a variant to match the surface: outline for forms, secondary to sit back on a busy panel, ghost for inline edits in a table cell.

Processing
24 open tickets
High
Team languages
Marta Villoresi
Italian
Lukas Brandt
English

variant="none" is a blank field you style yourself, focus ring included, like the flat search field on BbTextInput.

For a one-off, reach a part with pt: pt:activator is the button that opens the list, pt:input the search field in the panel, pt:badge each chip. Chip and option parts also take a function of the item. On a phone, the sheet's own chip row does not apply pt:badge.

Lukas BrandtTomas DelgadoStruck-through approvers have left the team.

pt:panel, pt:option, pt:header and pt:footer style the options surface, its rows and the bands around the header and footer slots (on a phone, the sheet's own).

Coming from v2

pt:input on BbSelect used to be the field's button; that part is now pt:activator, and pt:input is the search field. Option classes moved from autocomplete-option* to bb-listbox__option*, .bb-select__chip is .bb-select__badge and .bb-select__comma-item is .bb-select__comma. --bb-select-option-px and --bb-select-option-py are removed: set row heights with props, not CSS.