Select
Select lets users choose a single option from a predefined list, presented as a closed field that expands into a menu on interaction.
Show 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> );}Floating label
Section titled “Floating label”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 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> );}Empty first option
Section titled “Empty first option”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.
Custom styled select
Section titled “Custom styled select”Add className="custom-select" to give the list of options the Volvo styling.
Show 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 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> );}HTML without React
Section titled “HTML without React”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.
Validation
Section titled “Validation”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.
Localization
Section titled “Localization”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.
Accessibility
Section titled “Accessibility”Key responsibilities from the WCAG audit:
- Always provide a label:
Selectrequires alabelprop. Never omit it. - Error association:
errorMessageautomatically setsaria-invalidand wiresaria-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.
| Prop | Type | Required | Default |
|---|---|---|---|
variant | FormFieldVariant | - | top-label |
required | boolean | - | - |
| 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. | |||
name | string | ✓ | - |
| The name of the input to use when submitting the form. | |||
label | string | ✓ | - |
| 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 | |||
errorMessage | string | - | - |
| Set the error message of a select and mark it invalid. | |||
aria-invalid | boolean | - | false |
| Force the input to be invalid. | |||
defaultValue | string | string[] | - | - |
| Default value of an uncontrolled input. | |||
value | string | string[] | - | - |
| Value of the input. Makes the input controlled. | |||
onChange | ChangeEventHandler<HTMLSelectElement, Element> | - | - |
| Fires when the select’s value is changed. | |||
hidden | boolean | - | - |
id | string | - | - |
title | string | - | - |
dir | string | - | - |
lang | string | - | - |
slot | string | - | - |
translate | "yes" | "no" | - | - |
className | string | - | - |
style | CSSProperties | - | - |
tabIndex | number | - | - |
onPointerDown | PointerEventHandler<Element> | - | - |
onPointerEnter | PointerEventHandler<Element> | - | - |
onPointerLeave | PointerEventHandler<Element> | - | - |
onPointerMove | PointerEventHandler<Element> | - | - |
onPointerUp | PointerEventHandler<Element> | - | - |
disabled | boolean | - | 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. | |||
autoFocus | boolean | - | false |
| Gives the input focus on page load. Use sparingly as it can be confusing to screen-reader and mobile users. | |||
readOnly | boolean | - | false |
| Makes the input read-only. Use sparingly, it's often preferred to present the data as regular text or in a table instead. | |||
form | string | - | - |
| Id of a form element that this input should be associated with. Defaults to the containing form element. | |||
hint | ReactNode | - | - |
| Additional hint or description. Supports text, links and block content such as paragraphs or Markdown output. | |||
aria-describedby | string | - | - |
aria-labelledby | string | - | - |
onInvalid | FormEventHandler<HTMLSelectElement> | - | - |
| Fires if the input fails validation on form submit. | |||
onFocus | FocusEventHandler<HTMLSelectElement> | - | - |
onBlur | FocusEventHandler<HTMLSelectElement> | - | - |
onKeyDown | KeyboardEventHandler<HTMLSelectElement> | - | - |
onKeyUp | KeyboardEventHandler<HTMLSelectElement> | - | - |