Sizes
Small
Dense UI, compact forms. The box is 20px — see Target size under Accessibility.
<Checkbox size="sm" label="Small" defaultChecked />Medium
The default size for most layouts.
<Checkbox size="md" label="Medium" defaultChecked />Large
Prominent, standalone choices.
<Checkbox size="lg" label="Large" defaultChecked />Disabled
Disabled, unchecked
<Checkbox label="Accept terms" disabled />Disabled, checked
<Checkbox label="Accept terms" disabled defaultChecked />Hover on the box
Hover the box
Figma's Checkbox spec defines no hover state — this is a deliberate departure from Figma, not a Figma match. Hovering the box layers a 5% black overlay (the global overlay/hover/dark token) over the box's existing fill, so the checked and unchecked boxes each tint from their own colour. A disabled box does not change on hover. Hovering the label text alone does nothing — the overlay is on the box.
<Checkbox label="Accept terms" />
<Checkbox label="Accept terms" defaultChecked />With label
Label right (default)
The whole label row — box and text — is a click target.
<Checkbox label="Accept terms" />Label positions
Right (default)
<Checkbox label="Accept terms" labelPosition="right" />Left
<Checkbox label="Accept terms" labelPosition="left" />Top
<Checkbox label="Accept terms" labelPosition="top" />Below
<Checkbox label="Accept terms" labelPosition="below" />With descriptor
Label and descriptor
The descriptor is announced as the accessible description, separately from the label.
<Checkbox label="Accept terms" descriptor="You can withdraw your consent at any time." />Form usage
Form field
name and value submit with the form — the entry appears in FormData only when the box is checked. required blocks submission until it is.
<form>
<Checkbox name="terms" value="accepted" label="Accept terms" required />
</form>When to use
Use a checkbox for a single boolean choice that is part of a form — accepting terms, opting in to a list, filtering a set of results. Unlike a switch, a checkbox does not imply the change takes effect immediately: it is a value that is submitted with the form.
Because the web component renders a real <input type="checkbox">, it participates in normal form submission and HTML5 validation. That is the reason to reach for Checkbox over Switch.
When not to use
- A setting that applies immediately, with no save/submit step: use a switch instead — its on/off framing implies the change has already taken effect.
- A list of options where only one is correct at a time: use radio buttons.
- An indeterminate or tri-state value: not supported — the control is a bare true/false.
- A group of related checkboxes: not built yet (
Checkbox Group, coming soon); compose individual checkboxes for now.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| checked | boolean | - | Controlled checked state. Omit and use defaultChecked for uncontrolled usage. |
| defaultChecked | boolean | false | Initial checked state for uncontrolled usage. Ignored if checked is provided. |
| onChange | (checked: boolean) => void | - | Fires with the new checked value on toggle (click or Space). |
| size | 'sm' | 'md' | 'lg' | 'md' | Size of the box. The tick scales with it. |
| disabled | boolean | false | Disables interaction and dims the box and any label/descriptor text together. |
| label | string | - | Optional label text. When set, renders the composite (control + label + optional descriptor). The whole row is one click/tap target on both platforms — web wraps it in a <label>, native makes the text block pressable. |
| descriptor | string | - | Optional secondary text beneath the label. Only rendered when label is set. |
| labelPosition | 'left' | 'right' | 'top' | 'below' | 'right' | Position of the label relative to the control. Only used when label is set. |
| name | string | - | Web only. Form field name — the control is form-serializable. |
| value | string | - | Web only. Form field value. When checked, FormData includes name: value. |
| required | boolean | false | Web only. HTML5 validation: the box must be checked before form submission. |
| form | string | - | Web only. Associates the control with a <form id=""> elsewhere on the page. |
| id | string | - | Web only. Generated via useId() if omitted; derives the -label and -descriptor ids wired into aria-labelledby / aria-describedby. |
| aria-label | string | - | Accessible name when no visible label is set. Ignored when label is provided — the visible label wins. |
| accessibilityLabel | string | label | Native only. Overrides the accessible name; falls back to label. |
| testID | string | - | Native only. Sub-ids: -wrapper, -icon, -hover-overlay. |
Import
Web
import { Checkbox } from '@checkatrade/components-web';Native
import { Checkbox } from '@checkatrade/components-native';Basic usage
Controlled (both platforms):
const [accepted, setAccepted] = useState(false);
<Checkbox checked={accepted} onChange={setAccepted} label="Accept terms" />;Uncontrolled (both platforms):
<Checkbox defaultChecked label="Accept terms" />Form submission (web only — the reason to use Checkbox over Switch):
<form>
<Checkbox name="terms" value="accepted" label="Accept terms" required />
</form> Platform status
| Platform / Area | Status |
|---|---|
| Design (Figma) | Beta |
| Web (React) | Beta |
| Native (React Native) | Beta |
| iOS (Swift) | Planned |
| Android (Kotlin) | Planned |
| Accessibility audit | Planned |
Accessibility
Web
- Renders a real
<input type="checkbox">— visually hidden but focusable, so the control is Tab-reachable and toggles with Space. The styled box is decorative (aria-hidden="true"); state lives on the input. - With
label, the label text becomes the accessible name viaaria-labelledby, and the descriptor becomes the accessible description viaaria-describedby— announced separately, never concatenated into one string. - With
label, the whole<label>row is a click target, so clicking the text toggles the box. - Without a visible
label, passaria-labelto supply the accessible name — otherwise the control has none (consumer responsibility). labelPosition="left"is a visual reorder only (flex-row-reverse): the control stays first in DOM and assistive-tech traversal order.
React Native
- Renders a
PressablewithaccessibilityRole="checkbox"andaccessibilityState={{ checked, disabled }}.accessibilityLabelfalls back tolabelwhen set. - The label and descriptor text are rendered visually but hidden from assistive tech, so the control is announced once rather than twice.
- The text block is a press target. Pressing the label or descriptor toggles the checkbox through the control’s own handler, so box and text form one continuous tap target — the same behaviour as web. The press is a no-op when
disabled. The text is not an accessibility node, so the control stays the single announced checkbox.
Target size
- Web: the box is 20 / 24 / 32px at
sm/md/lg. With alabel, the whole row is the click target. - Native: with a
label, the whole row is the tap target, so the effective target is much larger than the box. Without alabel, the box alone is the target —size="sm"is then a 20×20 tap target, still below the 24×24 WCAG 2.2 SC 2.5.8 minimum. Flagged to Design alongside the touch-target policy for other controls.
No releases yet.