Checkbox
Checkboxes let users toggle a binary choice or select multiple options from a list. Use Checkbox from @volvo-cars/react-forms, or build with plain HTML using native <input type="checkbox"> elements styled automatically by @volvo-cars/css.
import { Checkbox } from '@volvo-cars/react-forms';
export function CheckboxBasic() { return ( <Checkbox name="updates" label="Send me product updates" defaultChecked /> );}The hint prop adds a description below the label, linked via aria-describedby. Use hints for supplementary context — place links here rather than in the label text.
Read the terms of service and privacy policy.
import { Checkbox } from '@volvo-cars/react-forms';
export function CheckboxWithHint() { return ( <Checkbox name="terms" label="Accept terms of service" hint={ <> Read the <a href="#terms">terms of service</a> and{' '} <a href="#privacy">privacy policy</a>. </> } /> );}Controlled and uncontrolled
Section titled “Controlled and uncontrolled”Uncontrolled
Section titled “Uncontrolled”Pass defaultChecked to pre-select a checkbox without managing state — see the opening example above.
Controlled
Section titled “Controlled”Pass checked and onChange when you need to read or drive the state externally.
import { Checkbox } from '@volvo-cars/react-forms';import { useState } from 'react';
export function CheckboxControlled() { const [checked, setChecked] = useState(false);
return ( <Checkbox name="updates" label="Send me product updates" hint="You can unsubscribe anytime." checked={checked} onChange={(event) => setChecked(event.target.checked)} /> );}Validation
Section titled “Validation”Set errorMessage to display an inline error and mark the checkbox invalid. The error is hidden when the checkbox is disabled. You can also force invalid styling with aria-invalid.
import { Checkbox } from '@volvo-cars/react-forms';
export function CheckboxError() { return ( <Checkbox name="terms" label="Accept terms of service" errorMessage="Must accept to proceed" /> );}Indeterminate
Section titled “Indeterminate”The indeterminate prop shows a visual “partially selected” state — useful for parent/child checkbox hierarchies. It is visual-only and does not affect the submitted value; you manage the checked state of each child yourself.
import { Checkbox } from '@volvo-cars/react-forms';import { useState } from 'react';
export function CheckboxIndeterminate() { const [news, setNews] = useState(true); const [offers, setOffers] = useState(false);
const allChecked = news && offers; const indeterminate = news !== offers;
return ( <div className="flex-col gap-16"> <Checkbox name="subscriptions" label="Subscriptions" checked={allChecked} indeterminate={indeterminate} onChange={(e) => { setNews(e.target.checked); setOffers(e.target.checked); }} /> <div className="flex-col gap-8 pl-24"> <Checkbox name="news" label="Product news" checked={news} onChange={(e) => setNews(e.target.checked)} /> <Checkbox name="offers" label="Special offers" checked={offers} onChange={(e) => setOffers(e.target.checked)} /> </div> </div> );}CSS-only usage
Section titled “CSS-only usage”@volvo-cars/css styles native <input type="checkbox"> elements automatically — no class needed. If you add any class to the input, include the checkbox class to keep design system styles.
<div class="flex flex-col gap-16"> <div class="flex-row"> <input type="checkbox" name="updates" id="updates" value="updates" /> <label class="ml-8" for="updates">Send me product updates</label> </div> <div class="flex-row"> <input type="checkbox" name="terms" id="terms" value="terms" checked /> <label class="ml-8" for="terms">I agree to the terms</label> </div> <div class="flex-row"> <input type="checkbox" name="offers" id="offers" value="offers" disabled /> <label class="ml-8" for="offers">Get special offers</label> </div></div>With hints
Section titled “With hints”Wrap the label and hint in a flex-col stack-4 container. Use micro text-secondary for hint text. Place links in the hint, not in the label.
Read the terms of service and privacy policy.
<div class="flex-row"> <input type="checkbox" name="terms" id="terms" value="terms" /> <div class="flex-col stack-4 ml-8"> <label for="terms">Accept terms of service</label> <p class="micro text-secondary"> Read the <a href="#terms">terms of service</a> and <a href="#privacy">privacy policy</a>. </p> </div></div>Error state
Section titled “Error state”Set aria-invalid="true" on the input and link an error message with aria-describedby.
<div class="flex-row"> <input type="checkbox" name="terms" id="terms-error" value="terms" aria-invalid="true" aria-describedby="terms-error-msg" /> <div class="flex-col stack-4 ml-8"> <label for="terms-error">Accept terms of service</label> <p id="terms-error-msg" class="micro text-feedback-red flex items-center"> Must accept to proceed </p> </div></div>Accessibility
Section titled “Accessibility”Key responsibilities from the WCAG audit:
- Label every checkbox —
Checkboxrequires alabelprop. For CSS-only, pair each<input>with a<label for>. - Keep labels plain text — add any required links in the
hintinstead. - Error association — use
errorMessage(React) oraria-invalid+aria-describedby(CSS-only) to communicate errors. - Pointer target size — the checkbox control is smaller than 44×44 px, but the adjacent label extends the clickable area.
- Status messages — VoiceOver in Safari has known issues announcing error messages on checkboxes (WCAG 4.1.3). Test with multiple screen readers.
| Prop | Type | Required | Default |
|---|---|---|---|
name | string | ✓ | - |
| The name of the input to use when submitting the form. | |||
label | string | ✓ | - |
| Concise label for the checkbox. Labels should not contain HTML, add any required links in the `hint` instead. | |||
value | string | - | on |
| The value of the checkbox that will be submitted with the form data. @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#value | |||
required | boolean | - | - |
| Makes the checkbox required. | |||
errorMessage | string | - | - |
| Set the error message of a checkbox and mark it invalid. | |||
aria-invalid | boolean | - | false |
| Force the checkbox to be invalid. | |||
checked | boolean | - | - |
| Whether the checkbox is checked. Makes the input controlled. | |||
onChange | ChangeEventHandler<HTMLInputElement, Element> | - | - |
| Fires when the input is checked or unchecked. | |||
defaultChecked | boolean | - | - |
| Whether an (uncontrolled) checkbox is checked by default. | |||
indeterminate | boolean | - | false |
| Indicates if the checkbox is in an indeterminate state, often used to show a "partially selected" state (e.g., when some child checkboxes are selected). This state is visual-only and does not affect the checkbox's submitted value. | |||
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. | |||
enterKeyHint | "done" | "go" | "next" | "previous" | "search" ... | - | - |
| Hint the browser about what label to show for the Enter button on mobile keyboards. | |||
aria-describedby | string | - | - |
aria-labelledby | string | - | - |
onInvalid | FormEventHandler<HTMLInputElement> | - | - |
| Fires if the input fails validation on form submit. | |||
onFocus | FocusEventHandler<HTMLInputElement> | - | - |
onBlur | FocusEventHandler<HTMLInputElement> | - | - |
onKeyDown | KeyboardEventHandler<HTMLInputElement> | - | - |
onKeyUp | KeyboardEventHandler<HTMLInputElement> | - | - |