> ## 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 – Code

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

<Tabs>
  <Tab title="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.
    </Warning>

    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.
  </Tab>

  <Tab title="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.

    <ParamField path="label" type="string">
      Visible label and accessible name. Omit it for an indicator-only checkbox,
      which then requires `aria-label` or `aria-labelledby`.
    </ParamField>

    <ParamField path="aria-label" type="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.
    </ParamField>

    <ParamField path="aria-labelledby" type="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.
    </ParamField>

    <ParamField path="analyticsId" type="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`.
    </ParamField>

    <ParamField path="defaultSelected" type="boolean" default="false">
      Initial selection for uncontrolled use. Ignored when `isSelected` is set.
    </ParamField>

    <ParamField path="isDisabled" type="boolean" default="false">
      Disables the checkbox.
    </ParamField>

    <ParamField path="isIndeterminate" type="boolean" default="false">
      Renders the mixed state for a select-all control. Presentation only — it
      does not change what a press reports.
    </ParamField>

    <ParamField path="isSelected" type="boolean">
      Controlled selected state.
    </ParamField>

    <ParamField path="onChange" type="(isSelected: boolean) => void">
      Fired with the next selected state on every press.
    </ParamField>

    <ParamField path="testID" type="string">
      Test hook forwarded to the underlying `Pressable`.
    </ParamField>
  </Tab>
</Tabs>
