codefast/ui

Command Palette

Search for a command to run...

Source
Form

Checkbox Group

Multi-select group of checkboxes sharing a value array. Supports disabled items.

Examples

Controlled multi-select

Several options share one value array; a disabled item is skipped.

Granted: read, write

32 lines
import { CheckboxGroup, CheckboxGroupItem } from "@codefast/ui/checkbox-group";
import { Label } from "@codefast/ui/label";
import { useState } from "react";

const PERMISSIONS = [
  { value: "read", label: "Read" },
  { value: "write", label: "Write" },
  { value: "delete", label: "Delete" },
  { value: "admin", label: "Admin", disabled: true },
];

export function CheckboxGroupPermissions() {
  const [selected, setSelected] = useState<Array<string>>(["read", "write"]);

  return (
    <div className="space-y-3">
      <CheckboxGroup className="gap-3" value={selected} onValueChange={(value) => setSelected(value ?? [])}>
        {PERMISSIONS.map(({ value, label, disabled }) => (
          <div key={value} className="flex items-center gap-2">
            <CheckboxGroupItem id={`perm-${value}`} value={value} {...(disabled ? { disabled } : {})} />
            <Label htmlFor={`perm-${value}`} className={disabled ? "opacity-50" : ""}>
              {label}
            </Label>
          </div>
        ))}
      </CheckboxGroup>
      <p className="text-xs text-ui-muted">
        Granted: <span className="font-medium text-ui-fg">{selected.join(", ") || "none"}</span>
      </p>
    </div>
  );
}

Horizontal layout

Lay items out in a wrapping row.

Working days: Mon, Wed, Fri

25 lines
import { CheckboxGroup, CheckboxGroupItem } from "@codefast/ui/checkbox-group";
import { Label } from "@codefast/ui/label";
import { useState } from "react";

const DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri"];

export function CheckboxGroupHorizontal() {
  const [days, setDays] = useState<Array<string>>(["Mon", "Wed", "Fri"]);

  return (
    <div className="space-y-3">
      <CheckboxGroup className="flex flex-wrap gap-4" value={days} onValueChange={(value) => setDays(value ?? [])}>
        {DAYS.map((day) => (
          <div key={day} className="flex items-center gap-2">
            <CheckboxGroupItem id={`day-${day}`} value={day} />
            <Label htmlFor={`day-${day}`}>{day}</Label>
          </div>
        ))}
      </CheckboxGroup>
      <p className="text-xs text-ui-muted">
        Working days: <span className="font-medium text-ui-fg">{days.join(", ") || "none"}</span>
      </p>
    </div>
  );
}

With descriptions

Pair each option with a secondary hint.

When someone replies to your thread.

When you’re @-mentioned anywhere.

A Monday summary of activity.

27 lines
import { CheckboxGroup, CheckboxGroupItem } from "@codefast/ui/checkbox-group";
import { Label } from "@codefast/ui/label";
import { useState } from "react";

const NOTIFICATIONS = [
  { value: "comments", label: "Comments", hint: "When someone replies to your thread." },
  { value: "mentions", label: "Mentions", hint: "When you’re @-mentioned anywhere." },
  { value: "digest", label: "Weekly digest", hint: "A Monday summary of activity." },
];

export function CheckboxGroupWithDescriptions() {
  const [value, setValue] = useState<Array<string>>(["mentions"]);

  return (
    <CheckboxGroup className="w-full max-w-xs gap-4" value={value} onValueChange={(next) => setValue(next ?? [])}>
      {NOTIFICATIONS.map((item) => (
        <div key={item.value} className="flex items-start gap-3">
          <CheckboxGroupItem id={`notify-${item.value}`} value={item.value} className="mt-0.5" />
          <div className="grid gap-0.5">
            <Label htmlFor={`notify-${item.value}`}>{item.label}</Label>
            <p className="text-xs text-ui-muted">{item.hint}</p>
          </div>
        </div>
      ))}
    </CheckboxGroup>
  );
}

Usage

The minimal import and composition — see Examples below for styled, real-world variants.

17 lines
import { CheckboxGroup, CheckboxGroupItem } from "@codefast/ui/checkbox-group";
import { Label } from "@codefast/ui/label";

export function CheckboxGroupUsage() {
  return (
    <CheckboxGroup defaultValue={["mon", "wed"]}>
      <div className="flex items-center gap-2">
        <CheckboxGroupItem id="mon" value="mon" />
        <Label htmlFor="mon">Monday</Label>
      </div>
      <div className="flex items-center gap-2">
        <CheckboxGroupItem id="wed" value="wed" />
        <Label htmlFor="wed">Wednesday</Label>
      </div>
    </CheckboxGroup>
  );
}

Anatomy

How the parts nest — every slot the component exposes, in composition order.

CheckboxGroup
└── CheckboxGroupItem

Features

  • Not built on Radix — a hand-rolled headless primitive, since Radix has no multi-select checkbox-group primitive.
  • Shares one value: string[] across every item — checking/unchecking updates array membership, not per-item state.
  • Individual items can be disabled while the rest of the group stays interactive.

API reference

Props for each part of the component. All native element props are also forwarded.

CheckboxGroup

A multi-selection group sharing one value array.

valuestring[]

The controlled list of checked values.

defaultValuestring[]

The checked values when initially rendered (uncontrolled).

onValueChange(value: string[]) => void

Called when the checked values change.

disabledboolean

Disables the whole group.

Defaultfalse

CheckboxGroupItem

valuestring

Added to the array when checked (required).

disabledboolean

Makes a single item non-selectable.

Defaultfalse

Accessibility

Built to be keyboard-navigable and screen-reader friendly out of the box.

KeyFunction
TabMoves between checkboxes.
SpaceToggles the focused item.
  • Each item is a checkbox with its own aria-checked.
  • Pair every item with a Label via matching id / htmlFor.
  • Use this over loose checkboxes when they share one value.

Guidelines

Conventions that keep usage consistent across an app.

Do

  • Use for choosing several options from a set (permissions, filters).
  • Keep a disabled item visible to signal an unavailable option.

Don’t

  • Don’t use for one-of-many — use Radio Group.
  • Don’t leave items unlabelled.

Explore further

Ready to integrate?

Follow the Getting Started guide to install @codefast/ui, or browse the full component gallery.