Skip to content

Select

Edit on GitHub

Select lets users choose a single option from a predefined list, presented as a closed field that expands into a menu on interaction.

@volvo-cars/css v3.1.0@volvo-cars/react-forms v3.1.0
Show codeHide code
import { Select } from '@volvo-cars/react-forms';
export function SelectBasic() {
return (
<Select name="model" label="Car model">
<option value="ex30">EX30</option>
<option value="ex90">EX90</option>
<option value="xc40">XC40</option>
<option value="xc60">XC60</option>
<option value="xc90">XC90</option>
<option value="xc70">XC70</option>
</Select>
);
}

Use variant="floating-label" to place the label inside the field, floating up when an option is selected. See Choosing a variant for when to use it.

Show codeHide code
import { Select } from '@volvo-cars/react-forms';
export function SelectFloatingLabel() {
return (
<Select name="model" label="Car model" variant="floating-label">
<option value="ex30">EX30</option>
<option value="ex90">EX90</option>
<option value="xc40">XC40</option>
<option value="xc60">XC60</option>
<option value="xc90">XC90</option>
</Select>
);
}

Without a defaultValue, Select renders a placeholder option with value="", selected until the user picks a real option. A top-label field shows “Select” in the page language, and a floating-label field shows its label. This prevents the browser from silently pre-selecting the first real option. Once a real option is selected, the placeholder is hidden and disabled so it can’t be selected again.

To show your own placeholder text, pass an <option value=""> child:

<Select name="model" label="Car model">
<option value="">Pick a model</option>
<option value="xc40">XC40</option>
</Select>

To start with a real option selected and no placeholder, pass defaultValue.

Add className="custom-select" to give the list of options the Volvo styling.

Show codeHide code
import { Select } from '@volvo-cars/react-forms';
export function SelectCustomStyled() {
return (
<Select name="model" label="Model" className="custom-select">
<option value="ex30">EX30</option>
<option value="ex90">EX90</option>
<option value="xc40">XC40</option>
<option value="xc60">XC60</option>
<option value="xc90">XC90</option>
</Select>
);
}
  • On small screens, the list opens as a bottom sheet over a scrim.
  • On larger screens, the list opens next to the field.

Scroll inside the list to see more options. Choose an option, tap the field again, tap outside the list, or press Escape to close it.

Unlike MultiSelect, Select has no open or defaultOpen prop. The browser only opens the list when the user interacts with the field.

Browsers that do not support the custom style show their usual select menu.

In the custom styled select, long text in the closed field stays on one line and ends with an ellipsis (…). Open the list to read the full label. Long options wrap onto more than one line, and the value sent with the form stays the same.

Show codeHide code
import { Select } from '@volvo-cars/react-forms';
export function SelectCustomLongOptions() {
return (
<div style={{ maxWidth: 280 }}>
<Select
name="model"
label="Car model"
className="custom-select"
defaultValue="ex90"
>
<option value="ex30">EX30</option>
<option value="ex90">
Volvo EX90 Twin Motor Performance Ultra with seven seats
</option>
<option value="xc90">XC90</option>
</Select>
</div>
);
}

With @volvo-cars/css loaded, add custom-select to the wrapper. To shorten long selected text with …, add a <button> containing <selectedcontent> as the first child of the <select>. The React component adds this markup for you.

<div class="custom-select flex-col items-start gap-8">
<label for="custom-car-model">Car model</label>
<select id="custom-car-model" name="model">
<button type="button">
<selectedcontent></selectedcontent>
</button>
<option value="ex30">EX30</option>
<option value="ex90">EX90</option>
<option value="xc90">XC90</option>
</select>
</div>

This HTML example uses the browser’s menu and reset behavior. Two browser bugs can still affect it:

  • On Chromium touch devices, tapping the open field again can reopen the list.
  • In WebKit, resetting the form can leave the old selected text displayed.

React Select includes JavaScript fixes for these cases. They are not part of the CSS package.

Pass errorMessage to display an error and mark the field invalid. The error is hidden when the field is disabled. Use aria-invalid to force the invalid state without a message.

When nothing is selected, a top-label field shows “Select” in the page language. Translate the text you pass in, such as label, options, hint and errorMessage.

See Localization for how the page language is chosen, including on the server.

Key responsibilities from the WCAG audit:

  • Always provide a label: Select requires a label prop. Never omit it.
  • Error association: errorMessage automatically sets aria-invalid and wires aria-errormessage. Don’t set these manually when using the prop.
  • Status messages: VoiceOver in Safari has a known issue announcing validation errors on selects. Test with multiple screen readers.
PropTypeRequiredDefault
variantFormFieldVariant-top-label
requiredboolean--
Makes the select required.
enterKeyHint"done" | "go" | "next" | "previous" | "search" ...--
Hint the browser about what label to show for the Enter button on mobile keyboards.
namestring✓-
The name of the input to use when submitting the form.
labelstring✓-
A concise label for the input.
autoComplete"off" | "on" | "organization-title" | "organiza...--
What type of information to autocomplete in the select. @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete
errorMessagestring--
Set the error message of a select and mark it invalid.
aria-invalidboolean-false
Force the input to be invalid.
defaultValuestring | string[]--
Default value of an uncontrolled input.
valuestring | string[]--
Value of the input. Makes the input controlled.
onChangeChangeEventHandler<HTMLSelectElement, Element>--
Fires when the select’s value is changed.
hiddenboolean--
idstring--
titlestring--
dirstring--
langstring--
slotstring--
translate"yes" | "no"--
classNamestring--
styleCSSProperties--
tabIndexnumber--
onPointerDownPointerEventHandler<Element>--
onPointerEnterPointerEventHandler<Element>--
onPointerLeavePointerEventHandler<Element>--
onPointerMovePointerEventHandler<Element>--
onPointerUpPointerEventHandler<Element>--
disabledboolean-false
Disables the input. Use sparingly as it can be non-obvious to users why an input has been disabled. Prefer showing validation messages and hints instead.
autoFocusboolean-false
Gives the input focus on page load. Use sparingly as it can be confusing to screen-reader and mobile users.
readOnlyboolean-false
Makes the input read-only. Use sparingly, it's often preferred to present the data as regular text or in a table instead.
formstring--
Id of a form element that this input should be associated with. Defaults to the containing form element.
hintReactNode--
Additional hint or description. Supports text, links and block content such as paragraphs or Markdown output.
aria-describedbystring--
aria-labelledbystring--
onInvalidFormEventHandler<HTMLSelectElement>--
Fires if the input fails validation on form submit.
onFocusFocusEventHandler<HTMLSelectElement>--
onBlurFocusEventHandler<HTMLSelectElement>--
onKeyDownKeyboardEventHandler<HTMLSelectElement>--
onKeyUpKeyboardEventHandler<HTMLSelectElement>--