# Checkbox Group

Choose any number of related options.

Notification channel checkboxes

[Complete TSX file](/examples/checkbox-group/default.tsx)

```tsx
"use client";

import { Checkbox } from "@/siteplane/components/checkbox";
import { CheckboxGroup } from "@/siteplane/components/checkbox-group";
import { Label } from "@/siteplane/components/label";

export default function Example() {
  return (
    <CheckboxGroup aria-label="Notification channels" defaultValue={["email"]}>
      <Label>
        <Checkbox value="email" />
        Email
      </Label>
      <Label>
        <Checkbox value="push" />
        Push notifications
      </Label>
      <Label>
        <Checkbox value="sms" />
        SMS
      </Label>
    </CheckboxGroup>
  );
}
```

## Installation

Complete [one-time setup](/docs/installation/) first.

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/checkbox-group
```

With npm: `npx shadcn@4.18.0 add @siteplane/checkbox-group`. [Registry JSON](/registry/checkbox-group.json) lists source files and dependencies.

For this page’s examples, also install:

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/button @siteplane/checkbox @siteplane/fieldset @siteplane/form @siteplane/input @siteplane/label
```

Field is installed with Input.

## Usage

```tsx
import { Checkbox } from "@/siteplane/components/checkbox";
import { CheckboxGroup } from "@/siteplane/components/checkbox-group";
import { Label } from "@/siteplane/components/label";

<CheckboxGroup aria-label="Notification channels" defaultValue={["email"]}>
  <Label>
    <Checkbox value="email" />
    Email
  </Label>
  <Label>
    <Checkbox value="sms" />
    SMS
  </Label>
</CheckboxGroup>;
```

## Composition

```text
CheckboxGroup
├── Label + Checkbox
└── Label + Checkbox
```

## Controlled selection

Pass `value` and `onValueChange` to own the selected values. Each checkbox needs a unique `value`; a disabled item keeps its state.

Controlled column visibility

[Complete TSX file](/examples/checkbox-group/controlled.tsx)

```tsx
"use client";

import { useState } from "react";
import { Checkbox } from "@/siteplane/components/checkbox";
import { CheckboxGroup } from "@/siteplane/components/checkbox-group";
import { Label } from "@/siteplane/components/label";

export default function Example() {
  const [value, setValue] = useState<string[]>(["name", "status"]);
  return (
    <CheckboxGroup
      aria-label="Visible columns"
      onValueChange={setValue}
      value={value}
    >
      <Label>
        <Checkbox disabled value="name" />
        Name
      </Label>
      <Label>
        <Checkbox value="status" />
        Status
      </Label>
      <Label>
        <Checkbox value="owner" />
        Owner
      </Label>
      <Label>
        <Checkbox value="updated" />
        Last updated
      </Label>
    </CheckboxGroup>
  );
}
```

## Parent checkbox

Add a checkbox with `parent` and list every item value in `allValues`. The parent checks or clears all items and shows the mixed state when only some are selected.

Parent checkbox for project permissions

[Complete TSX file](/examples/checkbox-group/select-all.tsx)

```tsx
"use client";

import { useId, useState } from "react";
import { Checkbox } from "@/siteplane/components/checkbox";
import { CheckboxGroup } from "@/siteplane/components/checkbox-group";
import { Label } from "@/siteplane/components/label";

const permissions = [
  { label: "Read", value: "read" },
  { label: "Comment", value: "comment" },
  { label: "Write", value: "write" },
];

export default function Example() {
  const labelId = useId();
  const [value, setValue] = useState<string[]>(["read", "comment"]);
  return (
    <CheckboxGroup
      allValues={permissions.map((permission) => permission.value)}
      aria-labelledby={labelId}
      onValueChange={setValue}
      value={value}
    >
      <Label id={labelId}>
        <Checkbox parent />
        Project access
      </Label>
      {permissions.map((permission) => (
        <Label className="ms-4" key={permission.value}>
          <Checkbox value={permission.value} />
          {permission.label}
        </Label>
      ))}
    </CheckboxGroup>
  );
}
```

## Form integration

Render the `Field` as a `Fieldset` so the legend names the group and the field’s `name` applies to every checkbox. The form submits one entry per checked value.

Checkbox group in a form

[Complete TSX file](/examples/checkbox-group/form.tsx)

```tsx
"use client";

import { Button } from "@/siteplane/components/button";
import { Checkbox } from "@/siteplane/components/checkbox";
import { CheckboxGroup } from "@/siteplane/components/checkbox-group";
import { Field, FieldItem, FieldLabel } from "@/siteplane/components/field";
import { Fieldset, FieldsetLegend } from "@/siteplane/components/fieldset";
import { Form } from "@/siteplane/components/form";

export default function Example() {
  return (
    <Form
      className="grid w-full max-w-sm gap-4"
      onSubmit={(event) => {
        event.preventDefault();
      }}
    >
      <Field name="channels" render={(props) => <Fieldset {...props} />}>
        <FieldsetLegend>Notification channels</FieldsetLegend>
        <CheckboxGroup defaultValue={["email"]}>
          <FieldItem>
            <FieldLabel>
              <Checkbox value="email" />
              Email
            </FieldLabel>
          </FieldItem>
          <FieldItem>
            <FieldLabel>
              <Checkbox value="push" />
              Push notifications
            </FieldLabel>
          </FieldItem>
          <FieldItem>
            <FieldLabel>
              <Checkbox value="sms" />
              SMS
            </FieldLabel>
          </FieldItem>
        </CheckboxGroup>
      </Field>
      <Button type="submit">Save preferences</Button>
    </Form>
  );
}
```

## Card style

Use `Label variant="card"` for options that need a description; each card toggles its own checkbox.

Add-on cards

[Complete TSX file](/examples/checkbox-group/card-style.tsx)

```tsx
"use client";

import { Checkbox } from "@/siteplane/components/checkbox";
import { CheckboxGroup } from "@/siteplane/components/checkbox-group";
import { Label } from "@/siteplane/components/label";

const addOns = [
  {
    description: "Dashboards and usage reports for every project.",
    title: "Analytics",
    value: "analytics",
  },
  {
    description: "A searchable record of security events.",
    title: "Audit log",
    value: "audit-log",
  },
  {
    description: "Replies from our team within four business hours.",
    title: "Priority support",
    value: "support",
  },
];

export default function Example() {
  return (
    <CheckboxGroup
      aria-label="Add-ons"
      className="w-full max-w-sm"
      defaultValue={["analytics"]}
    >
      {addOns.map((addOn) => (
        <Label
          variant="card"
          className="flex w-full items-start"
          key={addOn.value}
        >
          <Checkbox value={addOn.value} />
          <span className="flex flex-col gap-1">
            <span>{addOn.title}</span>
            <span className="text-muted-foreground text-xs">
              {addOn.description}
            </span>
          </span>
        </Label>
      ))}
    </CheckboxGroup>
  );
}
```

## Sizes

Set `size` on the group to size every checkbox; a checkbox’s own `size` still wins.

Checkbox group sizes

[Complete TSX file](/examples/checkbox-group/sizes.tsx)

```tsx
"use client";

import { Checkbox } from "@/siteplane/components/checkbox";
import { CheckboxGroup } from "@/siteplane/components/checkbox-group";
import { Label } from "@/siteplane/components/label";

const sizes = [
  ["sm", "small"],
  ["md", "medium"],
  ["lg", "large"],
  ["xl", "extra large"],
] as const;

export default function Example() {
  return (
    <div className="flex flex-wrap gap-8">
      {sizes.map(([size, name]) => (
        <CheckboxGroup
          aria-label={`Notification channels, ${name}`}
          defaultValue={["email"]}
          key={size}
          size={size}
        >
          <Label>
            <Checkbox value="email" />
            Email
          </Label>
          <Label>
            <Checkbox value="sms" />
            SMS
          </Label>
        </CheckboxGroup>
      ))}
    </div>
  );
}
```

## API reference

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| CheckboxGroup.size | sm \| md \| lg \| xl | provider / lg | Shared item size. |
| value / defaultValue | string[] | uncontrolled | Controlled or initial selection. |
| onValueChange | (value, eventDetails) => void | unset | Selection change callback. |
| allValues | string[] | unset | Every item value; required for a parent checkbox. |
| disabled | boolean | false | Disables the group. |

The group places its options 8 px apart. A `gap-*` utility in `className` replaces that, so a horizontal group sets its own, for example `className="flex-row gap-4"`.

`CheckboxGroupProps` and `CheckboxGroupPrimitive` are exported. Size is the only owned prop. Checkbox is installed transitively; mark the parent checkbox with its `parent` prop.

Other props, refs, validation and events follow [Base UI CheckboxGroup](https://base-ui.com/react/components/checkbox-group). Styled parts accept string className.

[Read the installed source](/source/checkbox-group).

## Usage notes

Space toggles each focused checkbox. Give the group and each item an accessible label.
