Checkbox

Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected.

Updated in eds-core: 1.12.0

Quick Start

Installation
npm install @adaptavant/eds-core
Import
import { Checkbox } from '@adaptavant/eds-core';

Size

Customise the size of the Checkbox via the size prop.

{/* large */}
<Checkbox label="Check me!" size="large" />

{/* standard */}
<Checkbox label="Check me!" size="standard" />

Disabled

Use the isDisabled prop to show that a Checkbox isn't usable.

<Checkbox isDisabled label="Check me!" />

Invalid

Use the errorMessage prop to show that a Checkbox has an error associated with it.

The errorMessage prop is ⚠️ deprecated and will be removed in a future release, please check the FieldMessage section to add error state.

<Checkbox errorMessage="Invalid selection" label="Check me!" />

Status

Use the status prop to indicate whether a field is in an error or warning state.

<Stack className="gap-2">
  <Checkbox status="error" label="Check me!" />
  <Checkbox status="warning" label="Check me!" />
</Stack>

FieldMessage

Use the status and fieldMessage props to convey why the field is in an error or warning state. The message is announced on focus for screen readers.

<Stack className="gap-2">
  <Checkbox
    status="error"
    fieldMessage="Invalid selection"
    label="Check me!"
  />
  <Checkbox
    status="warning"
    fieldMessage="Invalid selection"
    label="Check me!"
  />
</Stack>

Controlled

A controlled Checkbox component uses React’s state and a callback function to update the state.

Status: Unchecked ❌
  const [selected, setSelected] = React.useState(false);
  const handleChange = (e) => setSelected(e.target.checked);

  return (
    <Stack>
      <Checkbox label="Check me!" onChange={handleChange} />
      <Text className="text-body-12"> Status: {selected ? "Checked ✅  " : "Unchecked ❌"}</Text>
    </Stack>
  );

Uncontrolled

An uncontrolled Checkbox component uses ref prop to access the current selected state. Additionally, you can maintain a React state to update other UI elements if needed.

Status: Checked ✅
  const checkboxRef = React.useRef(null);
  const [selected, setSelected] = React.useState(true);
  const handleChange = () => {
    if (checkboxRef.current) {
      setSelected(checkboxRef.current.checked);
    }
  };
  const handleClick = () => {
      alert(`My value is: "${checkboxRef.current.checked}"`);
  };

  return (
    <Stack className="gap-2">
      <Checkbox defaultChecked={true} label="Check me!" onChange={handleChange} ref={checkboxRef} />
      <Text className="text-body-12"> Status: {selected ? "Checked ✅  " : "Unchecked ❌"} </Text>
      <Button onClick={handleClick}>Click me!</Button>
    </Stack>
);

Indeterminate

The Checkbox component includes an indeterminate prop, designed for scenarios like header groups where some, but not all, options are selected. This is particularly useful for select-all checkboxes in group headers, as it visually indicates a partially selected state when only a subset of items is checked.

  const [checkedItems, setCheckedItems] = React.useState({
    1: true, // By default checked (makes header checkbox render in indeterminate state)
    2: false,
    3: false,
    4: false,
  });

  const [headerDisabled, setHeaderDisabled] = React.useState(false); // toggle this to make header checkbox disabled

  const disabledItems = {
			1: false,
			2: true, // By default disabled checkbox sub-item
			3: false,
      4: false,
		};

  const enabledItems = Object.keys(checkedItems).filter(
    (key) => !disabledItems[key]
  );

  const allChecked = enabledItems.every((key) => checkedItems[key]);
  const someChecked = enabledItems.some((key) => checkedItems[key]) && !allChecked;

  const handleHeaderChange = () => {
    const newCheckedState = !allChecked;
    setCheckedItems((prevState) => {
      const updatedState = { ...prevState };
      enabledItems.forEach((key) => {
        updatedState[key] = newCheckedState;
      });
      return updatedState;
    });
  };

  const handleItemChange = (id) => {
    setCheckedItems((prevState) => ({
      ...prevState,
      [id]: !prevState[id],
    }));
  };

  return (
    <Stack className="gap-2">
				<Checkbox
					checked={allChecked}
          isDisabled={headerDisabled}
					indeterminate={someChecked} // Visually indicates a partially selected state
					label="Select all"
					onChange={handleHeaderChange}
				/>
				<Stack className="pl-5">
					<Checkbox
						checked={checkedItems['1']}
						id="1"
            isDisabled={headerDisabled || disabledItems['1']} // When header is disabled, all sub-items should disabled
						label="Item 1"
						onChange={() => handleItemChange('1')}
					/>
					<Checkbox
						checked={checkedItems['2']}
						id="2"
            isDisabled={headerDisabled || disabledItems['2']} // By default disabled (header checkbox should not consider this for allchecked/somechecked state)
						label="Item 2"
						onChange={() => handleItemChange('2')}
					/>
					<Checkbox
						checked={checkedItems['3']}
						id="3"
            isDisabled={headerDisabled || disabledItems['3']}
						label="Item 3"
						onChange={() => handleItemChange('3')} // Ensure "id" is passed to handleItemChange
					/>
          <Checkbox
						checked={checkedItems['4']}
						id="4"
            isDisabled={headerDisabled || disabledItems['3']}
						label="Item 4"
						onChange={() => handleItemChange('4')}
					/>
				</Stack>
			</Stack>
);

Note: This is purely a visual change. It has no impact on whether the checkbox's value is used in a form submission. That is decided by the checked state, regardless of the indeterminate state.

Checkbox Position

Checkbox is placed at the start by default. Use checkboxPosition="start | end" prop to control the visual order of the checkbox and label.

This is a description
This is a description
<>
  <Checkbox
    label="Checkbox at start, Label at end"
    description="This is a description"
    checkboxPosition="start"
  />

  <Checkbox
    label="Label at start, Checkbox at end"
    description="This is a description"
    checkboxPosition="end"
    defaultChecked={true}
  />
</>

API Reference

Checkbox

PropTypeDescriptionDefault
labelReactNodeLabel for the checkbox describing its purpose._
isDisabled?booleanDisables the checkbox, making it non-interactive and visually styled as disabled.false
size?'standard'| 'large'The size of the checkbox.standard
value?stringDefines the value of the checkbox input, used when submitting a form._
checked?booleanIndicates whether the checkbox is checked, used in controlled components._
id?stringA unique ID for the checkbox._
name?booleanThe name attribute of the checkbox, useful for grouping in forms._
description?ReactNodeDescription text displayed alongside the checkbox, providing additional context._
errorMessage?ReactNodeMessage shown when the field is invalid, styled for error state and announced to screen-readers as invalid._
status?error | warningUse the status prop to indicate whether a field is in an error or warning state._
fieldMessage?stringUse the status and fieldMessage props to convey why the field is in an error or warning state. The message is announced on focus for screen readers.
aria-describedby?stringDefines the ID of the element that provides additional information about the checkbox for accessibility purposes._
onBlur?functionFunction called when the checkbox loses focus._
onClick?functionFunction called when the checkbox is clicked._
onChange?functionFunction called when the checkbox’s value changes._
indeterminate?booleanIndicates whether checkbox in an indeterminate state or not. Designed for scenarios like header groups where some, but not all, options are selected.false
checkboxPositionstart | endPlacement of the checkbox relative to the label. 'start' or 'end'.start

Note: For id and aria-describedby, if a value is provided, the IDs will be merged. Otherwise, React.useId will be used to generate unique IDs to associate the labels appropriately.

Style API

Our design system components include style props that allow you to easily customize different parts of each component to match your design needs.

Please refer to the Style API documentation for more insights.

Checkbox parts

Please enter a guide description
{/* Styling the label and description parts */}
<Checkbox
  label="Control Text Label"
  size="large"
  description="Please enter a guide description"
  className="bg-neutral-secondary"
  classNames={{
    checkboxPrimitiveControl: "bg-critical hover:bg-critical-hover",
    checkboxPrimitiveIcon: "bg-accent",
    label: "text-body-14 text-tertiary",
    descriptionTrack: "gap-1 ",
    description: "text-positive",
    spacer: "h-8",
  }}
/>

{/* Styling the error parts */}
<Checkbox
  label="Control Text Label"
  size="large"
  errorMessage="Error message goes here"
  classNames={{
    errorTrack: "text-caution",
    errorIcon: "text-caution",
    errorMessage: "text-caution",
  }}
/>
Stylable PartsDescription
rootThe root container of the checkbox, wrapping all inner components.
checkboxPrimitiveRootThe base container for the primitive checkbox structure.
checkboxPrimitiveInputThe input element for the checkbox, where the value is stored.
checkboxPrimitiveControlThe container for the control element, which visually represents the checked/unchecked state.
checkboxPrimitiveIconThe icon displayed within the checkbox when it is checked.
labelThe text label associated with the checkbox.
descriptionTrackWrapper for the description text when it appears alongside the checkbox.
descriptionDescription text providing additional information about the checkbox.
spacerA spacer element used to create a gap between the description text and the start of the description track to maintain alignment for description text and label.
errorTrackThe container for error messages when the checkbox is invalid.
errorIconIcon displayed to indicate an error state, typically styled to match the error message.
errorMessageText of the error message, providing feedback when validation fails.

Usage guidelines

Do

  • List, form, or table selections: Utilize checkboxes in lists, forms, or tables to present users with multiple, related options where selecting all, none, or some choices is necessary.
  • Accompanying field and InlineField components: Include checkboxes in forms alongside field and InlineField components or in constrained spaces where a toggle might be impractical due to size limitations.
  • Non-immediate selections: Opt for checkboxes when the user's selection does not take immediate effect and requires submission of a form for changes to occur.

Don’t

  • Exclusive choices: Avoid checkboxes in situations where users can only choose one option out of multiple, related choices. Instead, use a RadioGroup to enforce exclusivity.
  • Immediate selections, especially on mobile: Refrain from using checkboxes when a selection needs to take immediate effect, particularly in mobile contexts. Instead, employ a toggle for swift and direct interactions.
  • Ambiguous visual feedback: Steer clear of checkboxes when the visual distinction between turning a feature on or off is unclear. In such cases, a Toggle is recommended for its clarity in indicating the on/off state.

Best practices

Do

checkbox1

Employ checkboxes to enable the multi-selection of interconnected list items.

Don’t

checkbox2

Opt for checkboxes when allowing for a single selection is required. Alternatively, use a RadioGroup for this purpose.

Do

checkbox3

In forms where the selection becomes effective only after submitting, utilize a singular checkbox.

Don’t

checkbox4

Utilize a Checkbox for instant toggling of a state. Alternatively, use a Toggle for this purpose.

Do

checkbox5

Maintain concise and clear labels, legends and descriptions to prevent an overload of text that may impede scanning and slow down the user.

Don’t

checkbox6

Avoid using extensive text that gets truncated or fails to provide clear instructions on the expected selection.