> ## 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 - React Native

> The Checkbox selects one option, or a set of them, from a list.

> [!NOTE]
>
> For development, import `Checkbox` from
> `@servicetitan/anvil2-rn`. For design, find it in the
> [Mobile Agentic Component Library file](https://www.figma.com/design/gjGWnAOnI1kukfzjsUQ6dP/Mobile-Agentic-Component-Library).

**Implementation**

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

function App() {
  const [selected, setSelected] = useState(false);

  return (
    <Checkbox
      label="Notify customer"
      isSelected={selected}
      onChange={setSelected}
    />
  );
}
```

## Common Examples

Pass a visible `label` and control selection with `isSelected` and `onChange`. Use `defaultSelected` instead for uncontrolled selection. The kit forbids `children`.

```tsx theme={null}
<Checkbox label="Notify customer" isSelected={notify} onChange={setNotify} />
```

`isIndeterminate` renders the mixed state a select-all control needs. It is presentation only — a press still resolves to the opposite of `isSelected`, so the caller decides what selecting "all" means.

```tsx theme={null}
<Checkbox
  label="All business units"
  isSelected={allSelected}
  isIndeterminate={someSelected}
  onChange={(next) => setSelected(next ? [...units] : [])}
/>
```

Omit `label` for an indicator-only row — where a neighbouring cell is the visible name — and supply `aria-label`. TypeScript enforces it: a checkbox with no `label` and no `aria-label` will not compile.

```tsx theme={null}
<Checkbox aria-label="Select Coastal Air" isSelected={selected} onChange={toggle} />
```

> [!WARNING]
>
> **This differs from the web component on purpose.** Web takes `aria-label` *or* `aria-labelledby`; the RN kit requires `aria-label` and treats `aria-labelledby` as an optional extra.
>
> On React Native the two are not alternatives: `aria-labelledby` (RN's alias for `accessibilityLabelledBy`) is **Android-only**, and iOS has no labelledby concept — VoiceOver reads `accessibilityLabel`. Accepting `aria-labelledby` on its own would let a checkbox with no accessible name on iOS pass type-checking.
>
> Porting web code that named a checkbox with `aria-labelledby` alone is therefore a compile error here. That is intentional — the alternative is code that looks correct and is silently unusable for iOS screen-reader users.

Pass both to also associate the checkbox with its labelling element for TalkBack:

```tsx theme={null}
<Checkbox
  aria-label="Select Coastal Air"
  aria-labelledby="tenant-name" // Android only; never replaces aria-label
  isSelected={selected}
  onChange={toggle}
/>
```

## Accessibility

* The control uses `accessibilityRole="checkbox"`, with `aria-checked` set to `true`, `false`, or `"mixed"` when `isIndeterminate` is set. It uses `aria-checked` rather than `accessibilityState.checked` because the two are aliases on native, but only `aria-checked` survives react-native-web — without it the role ships with no checked state on web.
* A visible `label` is also set as `accessibilityLabel`; indicator-only rows are named by the required `aria-label`. `aria-labelledby` is an Android-only addition and never the name on its own — see the warning above.
* The whole row is the touch target, with a 40pt minimum — the indicator itself is 24pt, below every mobile platform's minimum. It is a laid-out minimum rather than `hitSlop`, so the accessibility frame matches the touch area.

**Checkbox Props**

```tsx theme={null}
<Checkbox label="Notify customer" isSelected={notify} onChange={setNotify} />
```

## `Checkbox` Props

The `Checkbox` accepts the following props. It does not forward remaining React Native `Pressable` props.

**label** `string`

Visible label and accessible name. Omit it for an indicator-only checkbox,
which then requires `aria-label` or `aria-labelledby`.

**aria-label** `string` *required*

Accessible name for an indicator-only checkbox — required whenever `label`
is omitted, and mutually exclusive with it. The only naming mechanism both
RN platforms support.

**aria-labelledby** `string`

Android only. Native id of the element that labels an indicator-only
checkbox, associating the two for TalkBack. Additive — it never replaces
`aria-label`, and iOS ignores it.

**analyticsId** `string`

Opt-in telemetry id. When set under `Anvil2RNTelemetryProvider`, a press
emits a `press` event with `meta.selected` (the state the press produced)
before `onChange`.

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

Initial selection for uncontrolled use. Ignored when `isSelected` is set.

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

Disables the checkbox.

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

Renders the mixed state for a select-all control. Presentation only — it
does not change what a press reports.

**isSelected** `boolean`

Controlled selected state.

**onChange** `(isSelected: boolean) =\> void`

Fired with the next selected state on every press.

**testID** `string`

Test hook forwarded to the underlying `Pressable`.


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