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

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

export const LiveCode = ({children, customHeight, clickToLoad, example, fullWidth, fullHeight, hideCodeInLiveCode, screenshot, screenshotOnly, showCode: showCodeProp}) => {
  const SCREENSHOTS_BASE = "https://servicetitan.github.io/anvil2-docs-live-code/screenshots";
  const STACKBLITZ_BASE = "https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples";
  const [showCodeBlock, setShowCodeBlock] = useState(showCodeProp ?? false);
  const [isLocalOverride, setIsLocalOverride] = useState(false);
  useEffect(() => {
    const examplePath = `/images/live-code-screenshots-tmp/${example}.png`;
    fetch(examplePath, {
      method: "HEAD"
    }).then(r => {
      if (r.ok) setIsLocalOverride(true);
    }).catch(() => {});
  }, [example]);
  const screenshotBase = isLocalOverride ? "/images/live-code-screenshots-tmp" : SCREENSHOTS_BASE;
  if (screenshotOnly) {
    return <Frame className="flex flex-col">
        <div className="flex dark:hidden" style={{
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined,
      background: "#FFFFFF"
    }}>
          <img srcset={`${screenshotBase}/${example}.png, ${screenshotBase}/${example}-2x.png 2x`} src={`${screenshotBase}/${example}.png`} alt={example} noZoom />
        </div>
        <div className="hidden dark:flex" style={{
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined,
      background: "#141414"
    }}>
          <img srcset={`${screenshotBase}/${example}-dark.png, ${screenshotBase}/${example}-dark-2x.png 2x`} src={`${screenshotBase}/${example}-dark.png`} alt={example} noZoom />
        </div>
      </Frame>;
  }
  if (screenshot) {
    return <Frame className="flex flex-col -mb-2">
        <div className="flex dark:hidden bg-white dark:bg-codeblock border border-gray-950/10 dark:border-white/10 dark:twoslash-dark rounded-2xl overflow-hidden" style={{
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined
    }}>
          <img srcset={`${screenshotBase}/${example}.png, ${screenshotBase}/${example}-2x.png 2x`} src={`${screenshotBase}/${example}.png`} alt={example} noZoom />
        </div>

        <div className="hidden dark:flex bg-white dark:bg-codeblock border border-gray-950/10 dark:border-white/10 dark:twoslash-dark rounded-2xl overflow-hidden" style={{
      background: "#141414",
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined
    }}>
          <img srcset={`${screenshotBase}/${example}-dark.png, ${screenshotBase}/${example}-dark-2x.png 2x`} src={`${screenshotBase}/${example}-dark.png`} alt={example} noZoom />
        </div>

        <div className="flex justify-end items-center text-xs py-2 px-1 gap-4">
          {!showCodeProp ? <button className="inline-flex justify-end items-center text-gray-700 dark:text-gray-50 hover:text-blue-500 dark:hover:text-blue-300 transition-colors group self-end gap-1 cursor-pointer" onClick={() => setShowCodeBlock(!showCodeBlock)} style={{
      appearance: "none"
    }}>
              <Icon icon="code" size="12px" className="group-hover:bg-blue-500 dark:group-hover:bg-blue-300" />
              <span>{showCodeBlock ? "Hide code" : "Show code"}</span>
            </button> : null}

          <a className="inline-flex justify-end items-center hover:text-blue-500 dark:hover:text-blue-300 transition-colors group self-end gap-1" href={`${STACKBLITZ_BASE}/${example}?file=src/App.tsx`} target="_blank" rel="noreferrer">
            <Icon icon="bolt" size="12px" className="group-hover:bg-blue-500 dark:group-hover:bg-blue-300" />
            <span>StackBlitz demo</span>
          </a>
        </div>

        <div className="grid transition-[grid-template-rows] duration-300 ease-in-out overflow-auto overflow-y-hidden overflow-x-auto" style={showCodeBlock ? {
      gridTemplateRows: "1fr"
    } : {
      gridTemplateRows: "0fr"
    }}>
          <div style={{
      minHeight: 0,
      overflowX: "auto",
      overflowY: "hidden",
      marginBlockStart: "-1.25rem",
      marginBlockEnd: "-1.5rem"
    }}>
            {children}
          </div>
        </div>
      </Frame>;
  } else {
    return <div style={{
      display: "flex",
      width: fullWidth ? "100%" : "50%",
      minHeight: customHeight ? customHeight : "316px",
      resize: "vertical",
      overflow: "auto"
    }}>
        <iframe title={example} style={{
      flex: 1,
      width: fullWidth ? "100%" : "50%",
      minHeight: customHeight ? customHeight : "316px"
    }} src={`${STACKBLITZ_BASE}/${example}?embed=1&hideNavigation=1&hideExplorer=1&terminalHeight=0&file=src/App.tsx${clickToLoad ? "&ctl=1" : ""}${hideCodeInLiveCode ? "&view=preview" : ""}`} allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" />
      </div>;
  }
};

<Tabs>
  <Tab title="Implementation">
    <LiveCode showCode example="checkbox-playground" fullWidth screenshot>
      ```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;
      ```
    </LiveCode>

    ## 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`.

    <LiveCode showCode example="checkbox-html" screenshot fullWidth>
      ```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;
      ```
    </LiveCode>

    ### Controlling checkbox state

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

    <LiveCode showCode example="checkbox-controlled" screenshot fullWidth>
      ```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;
      ```
    </LiveCode>

    ### Indeterminate checkboxes

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

    <LiveCode showCode example="checkbox-indeterminate" screenshot fullWidth>
      ```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;
      ```
    </LiveCode>

    ## 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](/docs/accessibility/labels-and-ctas#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` ``).

    <LiveCode showCode example="checkbox-group-markdownlabel" screenshot fullWidth>
      ```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;
      ```
    </LiveCode>

    ### 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.

    <LiveCode showCode example="checkbox-group-hidelabel" screenshot fullWidth>
      ```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;
      ```
    </LiveCode>
  </Tab>

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

    <ParamField path="checked" type="boolean" default="false">
      Controlled check state.
    </ParamField>

    <ParamField path="defaultChecked" type="boolean" default="false">
      Uncontrolled check state.
    </ParamField>

    <ParamField path="description" type="ReactNode">
      Helper text description below the checkbox.
    </ParamField>

    <ParamField path="error" type="boolean" default="false" />

    <ParamField path="icons" type="{ checked?: Svg, indeterminate?: Svg, unchecked?: Svg }">
      Overrides for default icons.
    </ParamField>

    <ParamField path="indeterminate" type="boolean" default="false">
      Sets the checkbox to an indeterminate state.
    </ParamField>

    <ParamField path="label" type="ReactElement | string">
      The label should have accessible string content.
    </ParamField>

    <ParamField path="onChange" type="(e: ChangeEvent<HTMLInputElement>, state?: CheckboxState) => void">
      Callback when the checkbox is changed.
    </ParamField>

    <ParamField path="onClick" type="(e?: MouseEvent<HTMLInputElement>, state?: CheckboxState) => void">
      Callback when the checkbox is clicked.
    </ParamField>
  </Tab>

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

    <ParamField path="legend" type="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.</Warning>
    </ParamField>

    <ParamField path="flexDirection" type={`"column" | "row"`} default="column">
      The direction that the checkboxes are organized. This is inherited from LayoutUtilProps.
    </ParamField>

    <ParamField path="hideLabel" type="boolean" default="false">
      Visually hides the legend while keeping it accessible to screen readers.
    </ParamField>

    <ParamField path="legendProps" type={`Omit<FieldLabelProps, "el">`}>
      Additional props to pass to the legend [FieldLabel](/docs/web/components/field-label/code) component. Supports the `aiMark` prop for displaying [AI-powered field indicators](/docs/web/utilities/ai-marks).
    </ParamField>

    <ParamField path="required" type="boolean" />
  </Tab>
</Tabs>
