Skip to main content

BwSelect| bw-select

Overview

BwSelect is a form control that allows the user to select one or more options from a list. It can be used as a single select or a multi select. Each item within the select should be a bw-option.

Usage

Basics

A select lets the user pick from a list of bw-options. Give it a label, an optional placeholder for the empty state, and a note for persistent helper text. Preselect an option with the value attribute (it must match an option's value).

<div class="flex-column gap">
<bw-select label="Country" placeholder="Select a country">
<bw-option value="us">United States</bw-option>
<bw-option value="ca">Canada</bw-option>
<bw-option value="mx">Mexico</bw-option>
</bw-select>

<bw-select label="Country (preselected)" value="ca" note="Defaults to Canada">
<bw-option value="us">United States</bw-option>
<bw-option value="ca">Canada</bw-option>
<bw-option value="mx">Mexico</bw-option>
</bw-select>
</div>

Reading the value

bw-select emits valueChange whenever the selection changes. Its detail is the selected value — a string for a single select, or an array for a multiple select. The event is scoped to the element (it does not bubble), so attach the listener directly to the bw-select.

<div class="flex-column gap">
<bw-select id="basicsSel" label="Favorite fruit" placeholder="Choose one">
<bw-option value="apple">Apple</bw-option>
<bw-option value="banana">Banana</bw-option>
<bw-option value="cherry">Cherry</bw-option>
</bw-select>
<bw-note id="basicsOut">valueChange: (nothing selected)</bw-note>
</div>
<script>
const select = document.querySelector('#basicsSel');
const out = document.querySelector('#basicsOut');
select.addEventListener('valueChange', e => {
out.textContent = 'valueChange: ' + e.detail;
});
</script>

Disabled

disabled prevents all interaction and removes the control from form submission. readonly keeps the value visible and submittable but blocks changes.

<div class="flex-column gap">
<bw-select label="Disabled" value="ca" disabled>
<bw-option value="us">United States</bw-option>
<bw-option value="ca">Canada</bw-option>
</bw-select>

<bw-select label="Read only" value="ca" readonly>
<bw-option value="us">United States</bw-option>
<bw-option value="ca">Canada</bw-option>
</bw-select>
</div>

Forms

bw-select is a form-associated control. Give it a name, place it in a bw-form (or native <form>), and its value is included on submit — a string for a single select, or a comma-joined string for a multiple select.

<bw-form id="roleForm" class="flex-column gap">
<bw-select name="role" label="Role" required placeholder="Select a role">
<bw-option value="admin">Admin</bw-option>
<bw-option value="editor">Editor</bw-option>
<bw-option value="viewer">Viewer</bw-option>
</bw-select>
<bw-button type="submit">Submit</bw-button>
<bw-note id="roleOut">Submit the form to see its data.</bw-note>
</bw-form>
<script>
const form = document.querySelector('#roleForm');
const out = document.querySelector('#roleOut');
form.addEventListener('dataSubmit', e => {
out.textContent = 'dataSubmit: ' + JSON.stringify(e.detail);
});
</script>

Required

A required select is invalid until something is selected, and adds an asterisk to its label. bw-form won't submit while it's empty. The error message appears once the user has interacted with the control.

<bw-form class="flex-column gap">
<bw-select name="plan" label="Plan" required placeholder="Choose a plan">
<bw-option value="free">Free</bw-option>
<bw-option value="pro">Pro</bw-option>
</bw-select>
<bw-button type="submit">Continue</bw-button>
</bw-form>

Custom validation

For rules the required attribute can't express, call the setCustomValidity(message) method — pass a message to mark the control invalid, or null to clear it. In this example only "Two" is accepted.

<bw-select id="cvSelect" label="Pick the number two" placeholder="Choose">
<bw-option value="1">One</bw-option>
<bw-option value="2">Two</bw-option>
<bw-option value="3">Three</bw-option>
</bw-select>
<script>
const select = document.querySelector('#cvSelect');
select.addEventListener('valueChange', e => {
select.setCustomValidity(e.detail === '2' ? null : 'Please choose Two');
});
</script>

Styling validity with CSS states

Like other form controls, bw-select exposes CSS custom states you can target: --required, --optional, --valid, --invalid, and — only after the user has interacted — --user-valid and --user-invalid.

<div>
<style>
@scope {
bw-select:state(--user-valid)::part(button) {
border: solid 1px var(--bw-green-500);
}
}
</style>
<bw-select label="Turns green once chosen" required placeholder="Choose one">
<bw-option value="1">One</bw-option>
<bw-option value="2">Two</bw-option>
</bw-select>
</div>

See the Forms tutorial for the full picture, including edit forms, dataPatch, and how an invalid form blocks submission and focuses the first invalid control.

Icons

bw-select can show status indicators in the trigger. These are purely visual — they communicate state but have no effect on form validation.

  • pending — shows a loading spinner (e.g. while options are loading).
  • success — shows a green check.
  • error — shows a red error icon. Set it to true for the icon alone, or to a string to show that message in a tooltip on hover.
<div class="flex-column gap">
<bw-select label="Loading options" placeholder="Loading…" pending>
<bw-option value="1">One</bw-option>
</bw-select>

<bw-select label="Confirmed" value="1" success>
<bw-option value="1">One</bw-option>
</bw-select>

<bw-select label="With error icon" placeholder="Choose…" error="true">
<bw-option value="1">One</bw-option>
</bw-select>

<bw-select label="With error tooltip" placeholder="Choose…" error="Please make a selection">
<bw-option value="1">One</bw-option>
</bw-select>
</div>

For validation errors tied to form constraints, rely on forms instead — the control renders its own error message automatically once the user has interacted with it.

Interface

By default the options open in a popover on wide screens and a bottom sheet on narrow ones (≤768px wide), which keeps the control comfortable to use on touch devices. Set the interface attribute to force one regardless of screen size.

<div class="flex-column gap">
<bw-select label="Popover" interface="popover" placeholder="Opens in a popover">
<bw-option value="1">One</bw-option>
<bw-option value="2">Two</bw-option>
<bw-option value="3">Three</bw-option>
</bw-select>

<bw-select label="Bottom sheet" interface="sheet" placeholder="Opens in a bottom sheet">
<bw-option value="1">One</bw-option>
<bw-option value="2">Two</bw-option>
<bw-option value="3">Three</bw-option>
</bw-select>
</div>

Multiple

Add the multiple attribute to let the user choose more than one option. The value becomes an array of strings, selected options appear as removable badges in the trigger, and a Select All / Deselect All shortcut is added to the list.

<bw-select multiple label="Toppings" placeholder="Choose toppings">
<bw-option value="cheese">Cheese</bw-option>
<bw-option value="pepperoni">Pepperoni</bw-option>
<bw-option value="mushroom">Mushroom</bw-option>
<bw-option value="onion">Onion</bw-option>
<bw-option value="pepper">Green pepper</bw-option>
</bw-select>

When every option is selected the trigger collapses to "All", and when there are more badges than fit, the overflow is summarized as "+N". Remove a selection by clicking the badge's close button.

Preselecting a multiple select

Because the value is an array, set it in JavaScript rather than as an attribute.

<bw-select id="multiPre" multiple label="Toppings">
<bw-option value="cheese">Cheese</bw-option>
<bw-option value="pepperoni">Pepperoni</bw-option>
<bw-option value="mushroom">Mushroom</bw-option>
<bw-option value="onion">Onion</bw-option>
</bw-select>
<script>
document.querySelector('#multiPre').value = ['cheese', 'mushroom'];
</script>

Options

Each item in a select is a bw-option. Its value is what the form submits; if you omit value, the option's text is used instead.

<bw-select label="Color" placeholder="Pick a color">
<bw-option>Red</bw-option>
<bw-option>Green</bw-option>
<bw-option>Blue</bw-option>
</bw-select>

Display value

Use display-value to show different text in the trigger than in the option list — handy when the list needs detail but the closed control should stay compact.

<bw-select label="Plan" value="pro" style="width: 400px;">
<bw-option value="free" display-value="Free">Free — for individuals</bw-option>
<bw-option value="pro" display-value="Pro">Pro — $9/month</bw-option>
<bw-option value="team" display-value="Team">Team — $20/user/month</bw-option>
</bw-select>

Disabled options

Mark an individual option disabled to keep it visible but unselectable.

<bw-select label="Plan" placeholder="Choose a plan" style="width: 400px;">
<bw-option value="free">Free</bw-option>
<bw-option value="pro">Pro</bw-option>
<bw-option value="enterprise" disabled>Enterprise (contact sales)</bw-option>
</bw-select>

Sizes

Use the size attribute to match the density of the surrounding UI. The default is small.

<div class="flex-column gap">
<bw-select size="small" value="1" label="Small">
<bw-option value="1">Small</bw-option>
<bw-option value="2">Another</bw-option>
</bw-select>
<bw-select size="medium" value="1" label="Medium">
<bw-option value="1">Medium</bw-option>
<bw-option value="2">Another</bw-option>
</bw-select>
<bw-select size="large" value="1" label="Large">
<bw-option value="1">Large</bw-option>
<bw-option value="2">Another</bw-option>
</bw-select>
</div>

Properties

PropertyAttributeDescriptionTypeDefault
disableddisabledMakes the input disabled and prevents changesbooleanfalse
errorerrorShows an error icon. If a string is passed in, it will render the icon as a tooltip. Has no effect on form validationanyundefined
interfaceinterfaceThe interface the control will be displayed on"popover" | "sheet"undefined
labellabelLabel that goes above the inputstringundefined
multiplemultipleAllows selecting multiple options and changes the value type to an arraybooleanfalse
notenoteInformational text directly below the controlstringundefined
originalValueoriginal-valueThe default value the control will reset to in a form. If not set, will default to the inital value of the "value" property.string | string[]undefined
pendingpendingShows a loading indicatorbooleanfalse
placeholderplaceholderPlaceholder for the inputstringundefined
readonlyreadonlyMakes the input readonly and prevents changesbooleanfalse
requiredrequiredMarks it as required in a form and shows an asterisk at the end of the labelbooleanfalse
sizesizeSize of the control"large" | "medium" | "small"'small'
successsuccessShows a success icon. Has no effect on form validationbooleanfalse
valuevalueThe currently selected value of the controls. Will be a string aray if it's a multiselectstring | string[]undefined

Events

EventDescriptionType
readyEmits once when the component has completed it's initial renderCustomEvent<any>
valueChangeEmits the currently selected values whenever they changeCustomEvent<string | string[]>

Methods

checkValidity() => Promise<boolean>

Checks the validity of the select

Returns

Type: Promise<boolean>

true if the select is valid, false otherwise

reportValidity() => Promise<boolean>

Reports the validity of the select

Returns

Type: Promise<boolean>

true if the select is valid, false otherwise

setCustomValidity(message: string | null) => Promise<void>

Sets the custom validity of the select

Parameters

NameTypeDescription
messagestring- The message to set the custom validity to

Returns

Type: Promise<void>

Slots

SlotDescription
"end"Content placed after selected value/s
"label"The label of the select
"note"The note of the select
"start"Content placed before selected value/s

Shadow Parts

PartDescription
"button"The button of the select
"main"The main div container of the select
"popover"The popover of the select
"sheet"The bottom sheet of the select

CSS Custom Properties

NameDescription
--heightHeight of the select
--input-colorColor of the selected value/s in the main container
--label-colorColor of the label
--padding-inlinePadding inline of the select
--placeholder-colorPlaceholder color of the select

Dependencies

Used by

Depends on

Graph


© 2025 United Systems & Software - All Rights Reserved.