> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Checkbox - Web

> Checkboxes are form elements that allow users to select one or more options.

> [!NOTE]
>
> For development, import `Checkbox` from
> `@servicetitan/anvil2`. For design, find it in the
> [Anvil2 file](https://www.figma.com/design/xaNQsvRKczoxu7pj0M6QT8/Anvil2-v3?node-id=24-2349).

![](https://mintcdn.com/servicetitan/uz2PQSvO75TRhQ38/images/docs/web/components/shared/overview-of-a-checkbox.png?fit=max\&auto=format\&n=uz2PQSvO75TRhQ38\&q=85\&s=68aac46a0454d9aec0890f442bf369df)

## Anatomy

The Checkbox consists of two primary elements that work together to allow users to select one or more options.

![Anatomy of
Checkboxes](https://mintcdn.com/servicetitan/Ni0bXIw9diilEZPZ/images/docs/web/components/checkbox/design/anatomy-of-checkboxes.png?fit=max\&auto=format\&n=Ni0bXIw9diilEZPZ\&q=85\&s=4012f597dc16f63547aea3337a23d2c9)

1. Checkbox
2. Label

## Options

The Checkbox supports selection states and description configurations to accommodate various form scenarios.

### Selection

checkbox-default

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-default?file=src/App.tsx)

```tsx lines theme={null}
import { Checkbox } from "@servicetitan/anvil2";

function App() {
  return (
    <Checkbox.Group legend="Group label">
      <Checkbox label="Unselected" />
      <Checkbox label="Selected" defaultChecked />
      <Checkbox label="Indeterminate" indeterminate />
    </Checkbox.Group>
  );
}

export default App;
```

Checkboxes can be unselected, selected, or indeterminate. Indeterminate checkboxes represent selected and non-selected values.

### Description

checkbox-description

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-description?file=src/App.tsx)

```tsx lines theme={null}
import { Checkbox } from "@servicetitan/anvil2";

function App() {
  return <Checkbox label="Label" description="Description text" />;
}

export default App;
```

Descriptions provide information to help the user avoid errors.

## Behavior

The Checkbox responds to user interaction with distinct visual states and flexible overflow handling.

### Visual States

#### Default

checkbox-default

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-default?file=src/App.tsx)

```tsx lines theme={null}
import { Checkbox } from "@servicetitan/anvil2";

function App() {
  return (
    <Checkbox.Group legend="Group label">
      <Checkbox label="Unselected" />
      <Checkbox label="Selected" defaultChecked />
      <Checkbox label="Indeterminate" indeterminate />
    </Checkbox.Group>
  );
}

export default App;
```

#### Disabled

checkbox-disabled

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-disabled?file=src/App.tsx)

```tsx lines theme={null}
import { Checkbox } from "@servicetitan/anvil2";

function App() {
  return (
    <Checkbox.Group legend="Group label">
      <Checkbox label="Unselected" disabled />
      <Checkbox label="Selected" disabled defaultChecked />
      <Checkbox label="Indeterminate" disabled indeterminate />
    </Checkbox.Group>
  );
}

export default App;
```

#### Error

checkbox-error

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-error?file=src/App.tsx)

```tsx lines theme={null}
import { Checkbox } from "@servicetitan/anvil2";

function App() {
  return (
    <Checkbox.Group legend="Group label">
      <Checkbox label="Unselected" error />
      <Checkbox label="Selected" error defaultChecked />
      <Checkbox label="Indeterminate" error indeterminate />
    </Checkbox.Group>
  );
}

export default App;
```

### Overflow

checkbox-overflow

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-overflow?file=src/App.tsx)

```tsx lines theme={null}
import { Checkbox } from "@servicetitan/anvil2";

function App() {
  return (
    <div style={{ width: "165px" }}>
      <Checkbox.Group legend="A longer legend that will wrap">
        <Checkbox label="Subscribe to weekly newsletter" />
      </Checkbox.Group>
    </div>
  );
}

export default App;
```

When a Checkbox label is too long for the available space, it will wrap to a new line.

## Usage Guidelines

| Standard | Requirement |
| - | - |
| DoMultiple selection options | Checkboxes give users a way to make zero, one, multiple, or all selections from a range of options. |

### Alternatives

#### Checkbox vs Combobox

Checkbox groups and the Combobox both allow multi-selection of a set of items. Checkbox groups should be used when there are only a few options. For larger options, Checkbox groups can still be used when there is space available and discovery of options is important. Comboboxes are good for 8 or more options and also when conserving space.

#### Checkbox vs Switch

Switches are used for making a selection between two mutually exclusive options and have an immediate effect. Checkboxes allow users to make zero to many choices from available options and changes are not immediate.

#### Checkbox vs Radio vs Switch

Radio buttons are used to select a single option from a set of mutually exclusive options. Checkboxes are best used to make zero to many selections from available options.

| | Checkbox | Switch | Radio |
| - | - | - | - |
| When is the selection executed? | After a user clicks a submit button | Immediately | After a user clicks a submit button |
| How many options are available? | 1 or more | 1 | 2 or more |
| Must an option be reducible to an on or off context? | No, it can be other things. | Yes | No, it can be other things. |
| If I had a group of this UI, what is the relation of each item in the group? | Independent of each other | Independent of each other | Mutually exclusive |
| Indeterminate state possible? | Yes | No | No |

### How to Use

#### Checkbox Groups

Sets of related Checkboxes should be grouped together in a Checkbox Group. In most cases, a Checkbox Group should have a label. Checkbox Groups allow users to see all available options that are related to each other.

## Content

Content within the Checkbox should clearly communicate the option being selected and provide necessary context.

### Give a descriptive, action-oriented label

The user relies on the label to provide context and identify what the checkbox controls. Use positive and active wording for checkbox labels. Include a verb to prompt the user to take action.

Use sentence case (capitalize only the first word and proper nouns) so control labels are easy to scan. Do not use periods for short phrases or single sentences.

Checkbox labels should follow the content guidelines for labels.

checkbox-action-oriented-do

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-action-oriented-do?file=src/App.tsx)

```tsx lines theme={null}
import { Checkbox, Flex } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex direction="column" gap="2">
      <Checkbox label="I agree to the terms of service" />
      <Checkbox label="Use the technician’s home business unit by default" />
      <Checkbox label="Allow changes" />
    </Flex>
  );
}

export default App;
```

> [!TIP]
>
> **Do**

checkbox-action-oriented-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-action-oriented-dont?file=src/App.tsx)

```tsx lines theme={null}
import { Checkbox, Flex } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex direction="column" gap="2">
      <Checkbox label="I Agree To The Terms of Service." />
      <Checkbox label="Use the technician’s home business unit by default." />
      <Checkbox label="Disable changes" />
    </Flex>
  );
}

export default App;
```

> [!CAUTION]
>
> **Don't**

### Give the user the information they need to make an informed decision

When the checkbox label doesn't provide enough information and context for users to make an informed decision, use inline help. Inline help works best because it requires no user interaction and is always visible. Keep inline help to 1-2 sentences.

Use the content guidelines for inline help.

Avoid using a tooltip to communicate this kind of information about an option. It's an additional step for users to mouse over the tooltip to reveal its information, and users can't refer to the tooltip content and take action at the same time.

checkbox-inline-help-do

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-inline-help-do?file=src/App.tsx)

```tsx lines theme={null}
import { Checkbox } from "@servicetitan/anvil2";

function App() {
  return (
    <Checkbox
      id="ex-content"
      label="Enable project labels"
      description="This allows techs to apply and edit project labels given to job records"
    />
  );
}

export default App;
```

> [!TIP]
>
> **Do**

## Keyboard Interaction

Users can navigate the Checkbox using standard keyboard controls.

| Key | Interaction |
| - | - |
| Space | Enables the selected Checkbox |
| Tab | Navigate between Checkboxes |

### Accessibility

* Checkboxes should have a visible label.
* Checkbox groups must have a legend that describes the group.
* If a Checkbox has an error, Helper Text must be present to describe the nature of the error.

For more guidance on form field labels and context, see [input field context association best practices](https://anvil.servicetitan.com/docs/accessibility/labels-and-ctas.md#input-field-context-association).

## Code

**Implementation**

checkbox-playground

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-playground?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Checkbox } from "@servicetitan/anvil2";

function App() {
  return (
    <Checkbox.Group legend="Choose fruit(s)" required onChange={console.log}>
      <Checkbox label="Apple" />
      <Checkbox label="Banana" />
      <Checkbox label="Strawberry" />
    </Checkbox.Group>
  );
}

export default App;
```

## Common Examples

```tsx theme={null}
import { Checkbox } from "@servicetitan/anvil2";

function ExampleComponent() {
  return (
    <Checkbox.Group
      direction="column"
      legend="Preferred methods of communication"
    >
      <Checkbox label="Email" defaultChecked />
      <Checkbox label="Phone" />
      <Checkbox label="Text" />
    </Checkbox.Group>
  );
}
```

### Basic checkbox inputs

The `Checkbox` component accepts many of the same props as an HTML `input` with `type="checkbox"`, such as `name`, `value`, `checked`, and `defaultChecked`.

checkbox-html

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-html?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Checkbox } from "@servicetitan/anvil2";

function App() {
  return (
    <Checkbox
      name="terms"
      label="I accept the terms and conditions."
      required
    />
  );
}

export default App;
```

### Controlling checkbox state

Use the `checked` and `onClick` props to manually control the state of a checkbox.

checkbox-controlled

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-controlled?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Checkbox, type CheckboxState } from "@servicetitan/anvil2";
import { useState } from "react";

function App() {
  const [list, setList] = useState([
    {
      label: "Email",
      checked: false,
    },
    {
      label: "Text",
      checked: true,
    },
  ]);

  const handleUpdate = (state: CheckboxState, index: number) => {
    const newArr = [...list];
    newArr[index].checked = !state.checked;
    setList(newArr);
  };

  return (
    <Checkbox.Group legend="Preferred method(s) of communcation" required>
      {list.map((item, i) => {
        return (
          <Checkbox
            key={i}
            checked={item.checked}
            label={item.label}
            onClick={(_e, state) => state && handleUpdate(state, i)}
          />
        );
      })}
    </Checkbox.Group>
  );
}

export default App;
```

### Indeterminate checkboxes

The `indeterminate` prop should be used to represent a checkbox that has checkbox children which some, but not all are checked.

checkbox-indeterminate

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-indeterminate?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Checkbox, Flex } from "@servicetitan/anvil2";
import { useState } from "react";

type ListItem = {
  label: string;
  checked: boolean;
  indeterminate?: boolean;
  childrenNodes?: ListItem[];
};

function App() {
  const [list, setList] = useState<ListItem[]>([
    {
      label: "Income statement",
      checked: false,
      childrenNodes: [
        {
          label: "Income",
          checked: false,
        },
        {
          label: "Expense",
          checked: false,
        },
      ],
    },
    {
      label: "Balance sheet",
      checked: true,
      indeterminate: true,
      childrenNodes: [
        {
          label: "Asset",
          checked: true,
        },
        {
          label: "Liabilities",
          checked: false,
        },
      ],
    },
  ]);

  // Note: this only works with 2 level
  const handleUpdate = (position: number, parent?: number) => {
    const newArr = [...list];
    if (typeof parent != "undefined") {
      const childrenNodes = newArr[parent].childrenNodes;
      if (
        childrenNodes &&
        Array.isArray(childrenNodes) &&
        childrenNodes.length > 0
      ) {
        childrenNodes[position].checked = !childrenNodes[position].checked;
        newArr[parent].childrenNodes = childrenNodes;

        // Check parent if all childrenNodes are checked
        if (childrenNodes.every((item) => item.checked)) {
          newArr[parent].indeterminate = false;
          newArr[parent].checked = true;
        }
        // Indeterminate parent if childrenNodes are partially checked
        else if (childrenNodes.some((item) => item.checked === true)) {
          newArr[parent].indeterminate = true;
        }

        // Uncheck parent if all childrenNodes are unchecked
        else if (childrenNodes.every((item) => item.checked === false)) {
          newArr[parent].checked = false;
          newArr[parent].indeterminate = false;
        }
      }
    } else {
      if (newArr[position].childrenNodes) {
        const newState = !newArr[position].checked;
        newArr[position].checked = newState;
        newArr[position].indeterminate = false;
        newArr[position].childrenNodes?.forEach(
          (item) => (item.checked = newState),
        );
      } else {
        newArr[position].checked = !newArr[position].checked;
      }
    }
    setList(newArr);
  };

  const createNodes = (list: ListItem[], parent?: number) => {
    return (
      <ul
        style={{
          listStyle: "none",
          paddingInlineStart: parent != null ? "2.5rem" : 0,
        }}
      >
        {list.map((item, i) => {
          return (
            <li key={i}>
              <Checkbox
                indeterminate={item.indeterminate}
                checked={item.checked}
                label={item.label}
                onClick={() => handleUpdate(i, parent)}
              />
              {item.childrenNodes &&
                Array.isArray(item.childrenNodes) &&
                item.childrenNodes.length > 0 &&
                createNodes(item.childrenNodes, i)}
            </li>
          );
        })}
      </ul>
    );
  };

  return (
    <Flex direction="column" gap="4">
      {createNodes(list)}
    </Flex>
  );
}

export default App;
```

## React Accessibility

* The `Checkbox` component's `label` prop allows for any `ReactElement` to be passed in, but the `label` should always be populated with some text content that is screen-reader accessible.

For more guidance on form field labels and context, see [input field context association best practices](https://anvil.servicetitan.com/docs/accessibility/labels-and-ctas.md#input-field-context-association).

### Markdown in legends

The `Checkbox.Group` `legend` prop supports inline markdown: bold (`**text**`), italic (`*text*`), bold and italic (`***text***`), highlight (`==text==`), and code (`` `text` ``).

checkbox-group-markdownlabel

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-group-markdownlabel?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Checkbox, Flex } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex direction="column" gap="6">
      <Checkbox.Group legend="**Bold** legend">
        <Checkbox label="Option A" />
        <Checkbox label="Option B" />
      </Checkbox.Group>
      <Checkbox.Group legend="*Italic* legend">
        <Checkbox label="Option A" />
        <Checkbox label="Option B" />
      </Checkbox.Group>
      <Checkbox.Group legend="***Bold and italic*** legend">
        <Checkbox label="Option A" />
        <Checkbox label="Option B" />
      </Checkbox.Group>
      <Checkbox.Group legend="==Highlight== legend">
        <Checkbox label="Option A" />
        <Checkbox label="Option B" />
      </Checkbox.Group>
      <Checkbox.Group legend="`Code` legend">
        <Checkbox label="Option A" />
        <Checkbox label="Option B" />
      </Checkbox.Group>
    </Flex>
  );
}

export default App;
```

### Hide the legend

Use `hideLabel` on `Checkbox.Group` to visually hide the legend. The `legend` string is kept as a screen-reader-only `<legend>` element so the group remains accessible — any inline markdown is stripped to plain text.

checkbox-group-hidelabel

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/checkbox-group-hidelabel?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Checkbox } from "@servicetitan/anvil2";

function App() {
  return (
    <Checkbox.Group legend="Notification preferences" hideLabel>
      <Checkbox label="Email" />
      <Checkbox label="SMS" />
      <Checkbox label="Push notifications" />
    </Checkbox.Group>
  );
}

export default App;
```

**Checkbox Props**

```tsx theme={null}
<Checkbox
  defaultChecked={true}
  label="Click me to select!"
  onChange={(e, state) => {
    console.log(e, state.checked, state.value);
  }}
/>
```

## `Checkbox` Props

**checked** `boolean` (default: false)

Controlled check state.

**defaultChecked** `boolean` (default: false)

Uncontrolled check state.

**description** `ReactNode`

Helper text description below the checkbox.

**error** `boolean` (default: false)

**icons** `\{ checked?: Svg, indeterminate?: Svg, unchecked?: Svg \}`

Overrides for default icons.

**indeterminate** `boolean` (default: false)

Sets the checkbox to an indeterminate state.

**label** `ReactElement | string`

The label should have accessible string content.

**onChange** `(e: ChangeEvent\<HTMLInputElement\>, state?: CheckboxState) =\> void`

Callback when the checkbox is changed.

**onClick** `(e?: MouseEvent\<HTMLInputElement\>, state?: CheckboxState) =\> void`

Callback when the checkbox is clicked.

**Checkbox.Group Props**

```tsx theme={null}
<Checkbox.Group direction="column" legend="Checkbox Group">
  <Checkbox label="Checkbox 1" defaultChecked />
  <Checkbox label="Checkbox 2" />
  <Checkbox label="Checkbox 3" />
</Checkbox.Group>
```

## `Checkbox.Group` Props

**legend** `ReactElement | string` *required*

The legend for the checkbox group. Supports inline markdown formatting.

> [!WARNING]
>
> Passing `ReactElement` is deprecated — use a plain string with inline markdown instead.

**flexDirection** `"column" | "row"` (default: column)

The direction that the checkboxes are organized. This is inherited from LayoutUtilProps.

**hideLabel** `boolean` (default: false)

Visually hides the legend while keeping it accessible to screen readers.

**legendProps** `Omit\<FieldLabelProps, "el"\>`

Additional props to pass to the legend [FieldLabel](https://anvil.servicetitan.com/docs/components/field-label/code) component. Supports the `aiMark` prop for displaying [AI-powered field indicators](https://anvil.servicetitan.com/docs/utilities/web/ai-marks.md).

**required** `boolean`


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.