Skip to content

Selection

Checkbox

Beta

A checkbox for a single boolean choice, available on web (React) and native (React Native). Three sizes, optional label and descriptor in four positions, and a real form-serializable input on web.

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 / AreaStatus
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 via aria-labelledby, and the descriptor becomes the accessible description via aria-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, pass aria-label to 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 Pressable with accessibilityRole="checkbox" and accessibilityState={{ checked, disabled }}. accessibilityLabel falls back to label when 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 a label, 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 a label, 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.